чување 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:
| M | README | | | 12 | ++++++++++++ |
| M | config.def.h | | | 26 | ++++++++++++++++---------- |
| M | draw.c | | | 80 | +++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------- |
| M | draw.h | | | 4 | ++-- |
| M | sled.1.in | | | 6 | ++++-- |
| M | sled.c | | | 25 | ++++++++++++++++++++++--- |
| M | util.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;
}