sled

Једноставни уређивач текста
git clone https://git.sr.ht/~strahinja/sled
Дневник | Датотеке | Референце | ПРОЧИТАЈМЕ | ЛИЦЕНЦА

чување 7ccd990370bcd921799dbf559f521e6fe38ada4f
родитељ 73728e4efb0b549d307381ea3e9b45b1c84894ff
Аутор: Страхиња Радић <sr@strahinja.org>
Датум:   Sun, 14 Jul 2024 15:49:44 +0200

Bugfix: display and handling of lines with wide characters; add a
segment to the status line showing the current codepoint

Diffstat:
MREADME | 12++++++++++++
Mconfig.def.h | 26++++++++++++++++----------
Mdraw.c | 80+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Mdraw.h | 4++--
Msled.1.in | 6++++--
Msled.c | 25++++++++++++++++++++++---
Mutil.c | 7++-----
измењених датотека: 7, додавања: 110(+), брисања: 50(-)

diff --git a/README b/README @@ -63,6 +63,18 @@ Q: How to get crontab -e to work? A: Set the backup_fail to 0 in config.h. If that fails, set create_backups to 0 in that file. +Q: Why are some wide characters covering the dialogs (help, search, replace)? +A: This is due to how wide characters work. The character is probably one cell + to the left of the border, and it needs two cells to be displayed. Writing + the border character one cell to the right is not enough, the previous + character would need to be overwritten by a single-cell character like Space. + Currently, termbox doesn't support getting the character from its display + buffer, which would enable testing for this case and handling it (but see + [1]). + + +[1]: https://github.com/termbox/termbox2/pull/65 + License ------- diff --git a/config.def.h b/config.def.h @@ -26,10 +26,12 @@ #define STATUS_1_BG TB_BLUE #define STATUS_2_FG TB_YELLOW | TB_BOLD #define STATUS_2_BG TB_BLUE -#define STATUS_3_FG TB_WHITE | TB_BOLD +#define STATUS_3_FG TB_WHITE #define STATUS_3_BG TB_BLUE -#define STATUS_4_FG TB_WHITE +#define STATUS_4_FG TB_WHITE | TB_BOLD #define STATUS_4_BG TB_BLUE +#define STATUS_5_FG TB_WHITE +#define STATUS_5_BG TB_BLUE #define TEXT_FG TB_DEFAULT #define TEXT_BG TB_DEFAULT #define TITLE_FG TB_BLACK @@ -50,12 +52,11 @@ extern const int wrap_width; * directory permissions? */ const int backup_fail = 1; const char* backup_suffix = "~"; -/* Comment out to not create backups */ -const int create_backups = 1; +const int create_backups = 1; /* comment out to not create backups */ const int enable_wrap = 1; const int line_numbers = 0; /* draw line numbers? initial value */ -/* - ideally, wrap_width + line_numbers_width <= 80 */ -/* - wrap width of 72 is recommended on https://useplaintext.email/ */ +/* - ideally, wrap_width + line_numbers_width (automatic) <= 80 */ +/* - wrap width of 72 is recommended on https://useplaintext.email/ */ const int wrap_width = 72; /* Always processed */ @@ -249,15 +250,20 @@ static const struct StatusSegment status_segments[] = { .fg = STATUS_2_FG, .bg = STATUS_2_BG, .alignment = CENTER}, - {.format = "%5ld:%5ld (%3ld%%)", - .callback = format_position, + {.format = "[%s]", + .callback = format_codep, .fg = STATUS_3_FG, .bg = STATUS_3_BG, .alignment = CENTER}, - {.format = "F1=HELP, C-Q=QUIT", - .callback = NULL, + {.format = "+%ld %ld:%ld (%ld%%)", + .callback = format_position, .fg = STATUS_4_FG, .bg = STATUS_4_BG, + .alignment = CENTER}, + {.format = "F1=HELP", + .callback = NULL, + .fg = STATUS_5_FG, + .bg = STATUS_5_BG, .alignment = RIGHT}, }; /* clang-format on */ diff --git a/draw.c b/draw.c @@ -110,9 +110,10 @@ assign_coord(struct Coord* dest, const struct Coord src) ssize_t buffer_length(uint32_t* buffer, const ssize_t max_index) { - ssize_t len = 0; uint32_t* pbuf = buffer; - ssize_t i = 0; + ssize_t len = 0; + ssize_t i; + int w; if (!pbuf) return 0; @@ -123,9 +124,12 @@ buffer_length(uint32_t* buffer, const ssize_t max_index) if (*pbuf == '\t') len += MIN(TAB_SIZE - (len % TAB_SIZE), max_index - len); else - len++; + { + w = wcwidth((wchar_t)*pbuf); + if (w > 0) + len += MIN(w, max_index - len); + } } - return i; } @@ -174,9 +178,11 @@ display_length(uint32_t* buffer, const ssize_t max_index) for (ssize_t i = 0; *pbuf && i < max_index; i++) { - pbuf = buffer + i; + pbuf = &buffer[i]; if (*pbuf == '\t') len += TAB_SIZE - (len % TAB_SIZE); + else if (!*pbuf) + len++; else { w = wcwidth((wchar_t)*pbuf); @@ -370,13 +376,15 @@ draw_screen(const struct DrawState* state) int ser = 0; int sec = 0; int maxx; + int fsc; /* First Shown Col */ if (!state) return 1; maxx = state->maxx; + fsc = state->first_shown.col; if (state->line_numbers) - maxx -= state->line_numbers_width; + maxx -= state->line_numbers_width + 2; if (state->rows_count == 0) return 0; @@ -417,17 +425,15 @@ draw_screen(const struct DrawState* state) ? state->line_numbers_width + 2 : 0, cy, TEXT_FG, TEXT_BG, current->text, maxx, maxx, - 0, 0, LEFT, state->first_shown.col, 0, - state->search, SEARCH_FG, SEARCH_BG, ssi, sei, - SEL_FG, SEL_BG); + 0, 0, LEFT, fsc, 0, state->search, SEARCH_FG, + SEARCH_BG, ssi, sei, SEL_FG, SEL_BG); i++; cy++; } - tb_set_cursor(state->line_numbers - ? state->line_numbers_width + 2 + state->display_column - - state->first_shown.col - : state->display_column - state->first_shown.col, + tb_set_cursor(state->line_numbers ? state->line_numbers_width + 2 + + state->display_column - fsc + : state->display_column - fsc, state->coord.row - state->first_shown.row); return 0; @@ -540,6 +546,20 @@ draw_string(const int x, const int y, const uint16_t fg, const uint16_t bg, } void +format_codep(char* result, ssize_t result_size, const char* format, + const ssize_t seg_size, const struct DrawState* state) +{ + char codep[LINE_DEFAULT]; + UNUSED(seg_size); + if (state->buffer[state->coord.row].text[state->coord.col]) + snprintf(codep, LINE_DEFAULT, "%06X", + state->buffer[state->coord.row].text[state->coord.col]); + else + memccpy(codep, "------", 0, LINE_DEFAULT); + snprintf(result, result_size, format, codep); +} + +void format_filename(char* result, ssize_t result_size, const char* format, const ssize_t seg_size, const struct DrawState* state) { @@ -602,8 +622,8 @@ format_position(char* result, ssize_t result_size, const char* format, UNUSED(seg_size); if (!result) return; - snprintf(result, result_size, format, state->display_column + 1, - state->coord.row + 1, + snprintf(result, result_size, format, state->first_shown.col, + state->display_column + 1, state->coord.row + 1, state->rows_count > 1 ? (state->coord.row * 100) / (state->rows_count - 1) : 0); @@ -756,7 +776,7 @@ redraw_status(struct DrawState* state) * index with screen * fill - boolean; 1 = fill fc, 0 = don't fill fc * srch - search string; if empty or NULL no highlighting happens - * sfg - fg color of search highlght + * sfg - fg color of search highlight * sbg - bg color of search highlight * ssi - selection start index (>=) * sei - selection end index (<) @@ -801,7 +821,7 @@ u32_draw_string(const int x, const int y, const uint16_t fg, const uint16_t bg, int ssc = 0; /* current string screen column; goes from start * of the string until ds+c */ - int ww = 0; /* wide character width */ + int w = 0; /* actual on-screen rune width */ const int ts = TAB_SIZE; @@ -943,27 +963,31 @@ u32_draw_string(const int x, const int y, const uint16_t fg, const uint16_t bg, } else { + w = wcwidth((wchar_t)*ps); + if (w < 0) + w = 1; if (ssc >= ds) { - if (sc < c) + if (sc < c && w > 0) { - ww = wcwidth((wchar_t)*ps); - if (ww < 0) - ww = 1; - if ((ww == 0) && (x + d + p_l + sc > 0)) - tb_extend_cell(x + d + p_l + sc + /*if ((w == 0) && (x + d + p_l + sc > + 0)) tb_extend_cell(x + d + p_l + sc - 1, y, *ps); - else - tb_set_cell(x + d + p_l + sc, y, - *ps, cfg, cbg); - sc += ww; + else*/ + /* TODO: Get the previous cell (if + * applicable) and overwrite it if + * it has wcwidth >1 + */ + tb_set_cell(x + d + p_l + sc, y, *ps, + cfg, cbg); + sc += w; } hctl--; if (hctl < 0) hctl = 0; } - ssc++; + ssc += w; } if (hctl == 0) { diff --git a/draw.h b/draw.h @@ -7,8 +7,6 @@ #include <unistd.h> #include <wchar.h> -#define TB_OPT_EGC - #include "termbox.h" #define TB_KEY_CTRL_SPACE 0x00 @@ -132,6 +130,8 @@ int draw_string(const int x, const int y, const uint16_t fg, const uint16_t bg, const int ds, const int fill, const uint32_t* srch, const uint16_t sfg, const uint16_t sbg, const int ssi, const int sei, const uint16_t slfg, const uint16_t slbg); +void format_codep(char* result, ssize_t result_size, const char* format, + const ssize_t seg_size, const struct DrawState* state); void format_filename(char* result, ssize_t result_size, const char* format, const ssize_t seg_size, const struct DrawState* state); void format_mode(char* result, ssize_t result_size, const char* format, diff --git a/sled.1.in b/sled.1.in @@ -178,10 +178,12 @@ The leftmost segment displays the current filename, with an asterisk .It Second segment displays the current mode. .It -Third segment displays the current position within the file, in the form +Third segment displays the Unicode codepoint at the position of the cursor. +.It +Fourth segment displays the current position within the file, in the form .Dl [column]:[row] ([percentage of total lines]%) .It -Fourth segment shows quick help. +Fifth segment shows quick help. .El . .Sh EXIT STATUS diff --git a/sled.c b/sled.c @@ -1675,6 +1675,10 @@ position_to(struct DrawState* state, const int row, const int column) { ssize_t dlen; int old_first_shown_row; + int maxx; + maxx = state->maxx; + if (state->line_numbers) + maxx -= state->line_numbers_width + 2; old_first_shown_row = state->first_shown.row; if (row != state->coord.row) { @@ -1742,10 +1746,24 @@ position_to(struct DrawState* state, const int row, const int column) state->saved_column = state->display_column; } if (state->first_shown.col > state->display_column) + { state->first_shown.col = state->display_column; - if (state->first_shown.col + (state->maxx - 1) < state->display_column) - state->first_shown.col - = state->display_column - (state->maxx - 1); + } + if (state->first_shown.col + maxx < state->display_column + 1) + { + int index_first; + int fsc = state->first_shown.col; + index_first + = buffer_length(state->buffer[state->coord.row].text, + fsc); + while (fsc + maxx < state->display_column + 1) + { + index_first++; + fsc = display_length(state->buffer[state->coord.row].text, + index_first); + } + state->first_shown.col = fsc; + } if (old_first_shown_row != state->first_shown.row) recalculate_line_numbers(state); @@ -2434,6 +2452,7 @@ static int toggle_line_numbers(struct DrawState* state) { state->line_numbers = !state->line_numbers; + position_to(state, state->coord.row, state->coord.col); return 0; } diff --git a/util.c b/util.c @@ -7,8 +7,6 @@ #include <stdlib.h> #include <string.h> -#define TB_OPT_EGC - #include "limit.h" #include "termbox.h" #include "util.h" @@ -110,15 +108,14 @@ u32_u8_memccpy(uint32_t* dst, const char* src, uint32_t c, size_t len) if (u8_len < 0) { /* error, replace uch with "error char" */ - uch = 0xFFFD; + uch = 0xFFFD; u8_len = -u8_len; } psrc += u8_len; copied += u8_len; if (copied >= len) return NULL; - } - while ((*dst++ = uch) != c && copied < len); + } while ((*dst++ = uch) != c && copied < len); return dst; }