line-ed: add unicode support

This commit is contained in:
2026-05-17 16:44:54 +01:00
parent 2f200e8d1b
commit 5a23da4cfe
9 changed files with 107 additions and 70 deletions
+9 -6
View File
@@ -1,12 +1,15 @@
#include "buffer.h"
#include "line-ed.h"
const char *line_start(struct line_ed *ed, size_t y)
#include <fx/cstr.h>
const fx_wchar *line_start(struct line_ed *ed, size_t y)
{
const char *line = ed->l_buf;
const fx_wchar *line = ed->l_buf;
for (size_t i = 0; i < y; i++) {
line += strcspn(line, "\n");
line += fx_wstrcspn(line, L"\n");
if (*line == '\n') {
line++;
@@ -18,10 +21,10 @@ const char *line_start(struct line_ed *ed, size_t y)
size_t line_length(struct line_ed *ed, size_t y)
{
const char *line = ed->l_buf;
const fx_wchar *line = ed->l_buf;
for (size_t i = 0; i < y; i++) {
line += strcspn(line, "\n");
line += fx_wstrcspn(line, L"\n");
if (*line == '\n') {
line++;
}
@@ -31,7 +34,7 @@ size_t line_length(struct line_ed *ed, size_t y)
return 0;
}
size_t len = strcspn(line, "\n");
size_t len = fx_wstrcspn(line, L"\n");
if (line[len] == '\n') {
len++;
}
+2 -1
View File
@@ -1,13 +1,14 @@
#ifndef LINE_ED_BUFFER_H_
#define LINE_ED_BUFFER_H_
#include <fx/encoding.h>
#include <stddef.h>
struct line_ed;
/* returns a pointer to the start of the line based on the given `y`
* coordinate */
extern const char *line_start(struct line_ed *ed, size_t y);
extern const fx_wchar *line_start(struct line_ed *ed, size_t y);
/* returns the length of the line based on the given `y` coordinate.
* for any line other than the last line in the buffer, this length
* INCLUDES the trailing linefeed. */
+22 -12
View File
@@ -19,29 +19,39 @@ void alloc_empty_history_entry(struct line_ed *ed)
void save_buf_to_history(struct line_ed *ed)
{
fx_string *cur
= (fx_string *)fx_array_get(ed->l_history, ed->l_history_pos);
fx_string_replace_all(cur, ed->l_buf);
fx_string *cur = (fx_string *)fx_array_get(
ed->l_history,
ed->l_history_pos);
fx_string_clear(cur);
fx_string_append_wstr(cur, ed->l_buf);
}
void append_buf_to_history(struct line_ed *ed)
{
fx_string *cur
= (fx_string *)fx_array_get(ed->l_history, ed->l_history_pos);
fx_string *cur = (fx_string *)fx_array_get(
ed->l_history,
ed->l_history_pos);
char s[] = {'\n', 0};
fx_string_append_cstr(cur, s);
fx_string_append_cstr(cur, ed->l_buf);
fx_string_append_wstr(cur, ed->l_buf);
}
void load_buf_from_history(struct line_ed *ed)
{
fx_string *cur
= (fx_string *)fx_array_at(ed->l_history, ed->l_history_pos);
size_t len
= MIN((size_t)(ed->l_buf_end - ed->l_buf - 1),
fx_string_get_size(cur, FX_STRLEN_NORMAL));
fx_string *cur = (fx_string *)fx_array_at(
ed->l_history,
ed->l_history_pos);
size_t len = MIN(
(size_t)(ed->l_buf_end - ed->l_buf - 1),
fx_string_get_size(cur, FX_STRLEN_CODEPOINTS));
const char *src = fx_string_get_cstr(cur);
for (size_t i = 0; i < len; i++) {
fx_wchar ch_src = fx_wchar_utf8_codepoint_decode(src);
ed->l_buf[i] = ch_src;
src += fx_wchar_utf8_codepoint_stride(src);
}
memcpy(ed->l_buf, fx_string_get_cstr(cur), len);
ed->l_buf[len] = '\0';
unsigned int x = 0, y = 0;
+27 -13
View File
@@ -8,7 +8,7 @@
#include <stdio.h>
void put_char(struct line_ed *ed, char c)
void put_char(struct line_ed *ed, fx_wchar c)
{
if (ed->l_buf_ptr > ed->l_line_end + 1) {
return;
@@ -21,8 +21,8 @@ void put_char(struct line_ed *ed, char c)
size_t prev_cursor = ed->l_buf_ptr - ed->l_buf;
char *dest = ed->l_buf_ptr;
size_t len = ed->l_line_end - ed->l_buf_ptr + 1;
fx_wchar *dest = ed->l_buf_ptr;
size_t len = (ed->l_line_end - ed->l_buf_ptr + 1) * sizeof *dest;
if (dest < ed->l_line_end) {
memmove(dest + 1, dest, len);
@@ -55,8 +55,8 @@ static void backspace_simple(struct line_ed *ed)
size_t prev_cursor = ed->l_buf_ptr - ed->l_buf;
char *dest = ed->l_buf_ptr;
size_t len = ed->l_line_end - ed->l_buf_ptr + 1;
fx_wchar *dest = ed->l_buf_ptr;
size_t len = (ed->l_line_end - ed->l_buf_ptr + 1) * sizeof *dest;
memmove(dest - 1, dest, len);
ed->l_cursor_x--;
@@ -78,8 +78,8 @@ static void backspace_nl(struct line_ed *ed)
.r_prev_line_len = prev_line_len,
};
char *dest = ed->l_buf_ptr;
size_t len = ed->l_line_end - ed->l_buf_ptr + 1;
fx_wchar *dest = ed->l_buf_ptr;
size_t len = (ed->l_line_end - ed->l_buf_ptr + 1) * sizeof *dest;
memmove(dest - 1, dest, len);
ed->l_cursor_x = prev_line_len - 1;
@@ -120,7 +120,8 @@ void cursor_left(struct line_ed *ed)
return;
}
if (ed->l_cursor_y <= ed->l_continuations || ed->l_buf_ptr <= ed->l_buf) {
if (ed->l_cursor_y <= ed->l_continuations
|| ed->l_buf_ptr <= ed->l_buf) {
return;
}
@@ -136,7 +137,10 @@ void cursor_left(struct line_ed *ed)
// printf("\033[A\033[%dG", len + prompt_len);
fx_tty_move_cursor_y(ed->l_tty, FX_TTY_POS_CURSOR, -1);
fx_tty_move_cursor_x(ed->l_tty, FX_TTY_POS_START, (int)(len + prompt_len));
fx_tty_move_cursor_x(
ed->l_tty,
FX_TTY_POS_START,
(int)(len + prompt_len));
fflush(stdout);
}
@@ -178,12 +182,17 @@ void arrow_up(struct line_ed *ed)
if (ed->l_cursor_y > 0) {
// printf("\033[%uA", ed->l_cursor_y);
fx_tty_move_cursor_y(ed->l_tty, FX_TTY_POS_CURSOR, (long long)ed->l_cursor_y);
fx_tty_move_cursor_y(
ed->l_tty,
FX_TTY_POS_CURSOR,
(long long)ed->l_cursor_y);
}
// printf("\033[%zuG\033[J", prompt_length(ed, PROMPT_MAIN) + 1);
fx_tty_move_cursor_x(
ed->l_tty, FX_TTY_POS_START, (long long)prompt_length(ed, PROMPT_MAIN));
ed->l_tty,
FX_TTY_POS_START,
(long long)prompt_length(ed, PROMPT_MAIN));
fx_tty_clear(ed->l_tty, FX_TTY_CLEAR_SCREEN | FX_TTY_CLEAR_FROM_CURSOR);
save_buf_to_history(ed);
@@ -202,12 +211,17 @@ void arrow_down(struct line_ed *ed)
if (ed->l_cursor_y > 0) {
// printf("\033[%uA", ed->l_cursor_y);
fx_tty_move_cursor_y(ed->l_tty, FX_TTY_POS_CURSOR, (int)ed->l_cursor_y);
fx_tty_move_cursor_y(
ed->l_tty,
FX_TTY_POS_CURSOR,
(int)ed->l_cursor_y);
}
// printf("\033[%zuG\033[J", prompt_length(ed, PROMPT_MAIN) + 1);
fx_tty_move_cursor_x(
ed->l_tty, FX_TTY_POS_START, prompt_length(ed, PROMPT_MAIN) + 1);
ed->l_tty,
FX_TTY_POS_START,
prompt_length(ed, PROMPT_MAIN) + 1);
save_buf_to_history(ed);
ed->l_history_pos++;
+3 -1
View File
@@ -1,9 +1,11 @@
#ifndef LINE_ED_INPUT_H_
#define LINE_ED_INPUT_H_
#include <fx/encoding.h>
struct line_ed;
extern void put_char(struct line_ed *ed, char c);
extern void put_char(struct line_ed *ed, fx_wchar c);
extern void backspace(struct line_ed *ed);
extern void cursor_left(struct line_ed *ed);
extern void cursor_right(struct line_ed *ed);
+15 -14
View File
@@ -6,6 +6,7 @@
#include "prompt.h"
#include <ctype.h>
#include <fx/cstr.h>
#include <fx/term/tty.h>
#include <stdio.h>
#include <stdlib.h>
@@ -98,13 +99,13 @@ static void clear_buffer(struct line_ed *ed)
ed->l_continuations = 0;
}
static void convert_continuation_feeds(char *s, size_t max)
static void convert_continuation_feeds(fx_wchar *s, size_t max)
{
char *end = s + max;
size_t len = strlen(s);
fx_wchar *end = s + max;
size_t len = fx_wstrlen(s);
while (1) {
size_t r = strcspn(s, "\\");
size_t r = fx_wstrcspn(s, L"\\");
if (s + r > end) {
break;
}
@@ -126,13 +127,13 @@ static void convert_continuation_feeds(char *s, size_t max)
}
}
static void remove_continuation_feeds(char *s, size_t max)
static void remove_continuation_feeds(fx_wchar *s, size_t max)
{
char *end = s + max;
size_t len = strlen(s);
fx_wchar *end = s + max;
size_t len = fx_wstrlen(s);
while (1) {
size_t r = strcspn(s, "\\");
size_t r = fx_wstrcspn(s, L"\\");
if (s + r > end) {
break;
}
@@ -156,9 +157,9 @@ static void remove_continuation_feeds(char *s, size_t max)
static bool input_is_empty(struct line_ed *ed)
{
const char *p = ed->l_buf;
const fx_wchar *p = ed->l_buf;
while (p < ed->l_line_end) {
if (!isspace(*p)) {
if (!fx_wchar_is_space(*p)) {
return false;
}
}
@@ -188,7 +189,7 @@ size_t line_ed_readline(struct line_ed *ed, fx_stringstream *out)
fputc('\r', stdout);
}
fputc(ed->l_buf[i], stdout);
fx_stream_write_char(fx_stdout, ed->l_buf[i]);
}
fflush(stdout);
@@ -257,7 +258,7 @@ size_t line_ed_readline(struct line_ed *ed, fx_stringstream *out)
arrow_down(ed);
break;
default:
if (iswgraph(key) || key == ' ') {
if (fx_wchar_is_graph(key) || key == ' ') {
put_char(ed, key);
}
break;
@@ -278,7 +279,7 @@ size_t line_ed_readline(struct line_ed *ed, fx_stringstream *out)
ed->l_history_pos = fx_array_size(ed->l_history) - 1;
const char *last = last_history_line(ed);
if (!last || strcmp(last, ed->l_buf) != 0) {
if (!last || fx_wastrcmp(last, ed->l_buf) != 0) {
save_buf_to_history(ed);
}
}
@@ -294,7 +295,7 @@ size_t line_ed_readline(struct line_ed *ed, fx_stringstream *out)
convert_continuation_feeds(ed->l_buf, sz);
}
fx_stream_write_cstr(out, ed->l_buf, NULL);
fx_stream_write_wstr(out, ed->l_buf, NULL);
fx_stream_write_char(out, '\n');
return sz;
+2 -2
View File
@@ -49,9 +49,9 @@ struct line_ed {
/* input buffer, pointer to the buffer cell that corresponds to
* the current cursor position, and pointer to the byte AFTER the last
* usable byte in the buffer */
char *l_buf, *l_buf_ptr, *l_buf_end;
fx_wchar *l_buf, *l_buf_ptr, *l_buf_end;
/* pointer to the byte AFTER the last byte of the user's input */
char *l_line_end;
fx_wchar *l_line_end;
/* 2-dimensional coordinates of the current cursor position.
* this does NOT include any prompts that are visible on the terminal */
size_t l_cursor_x, l_cursor_y;
+13 -12
View File
@@ -4,6 +4,7 @@
#include "cursor.h"
#include "line-ed.h"
#include <fx/cstr.h>
#include <fx/term/tty.h>
#include <stdio.h>
#include <stdlib.h>
@@ -15,19 +16,19 @@
* the (x, y) coordinates provided should be the data coordinates of the
* first character in `s`.
*/
void print_text(struct line_ed *ed, size_t x, size_t y, const char *s)
void print_text(struct line_ed *ed, size_t x, size_t y, const fx_wchar *s)
{
fx_tty *tty = ed->l_tty;
for (size_t i = 0; s[i] != '\n' && s[i] != '\0'; i++) {
fputc(s[i], stdout);
fx_stream_write_char(fx_stdout, s[i]);
}
}
void print_buffer(struct line_ed *ed)
{
const char *line_buf = ed->l_buf;
size_t line_len = strcspn(line_buf, "\n");
const fx_wchar *line_buf = ed->l_buf;
size_t line_len = fx_wstrcspn(line_buf, L"\n");
unsigned int y = 0;
@@ -44,7 +45,7 @@ void print_buffer(struct line_ed *ed)
}
y++;
line_len = strcspn(line_buf, "\n");
line_len = fx_wstrcspn(line_buf, L"\n");
fputc('\r', stdout);
fputc('\n', stdout);
@@ -74,8 +75,8 @@ void print_buffer(struct line_ed *ed)
void put_refresh(struct line_ed *ed, struct refresh_state *state)
{
/* get the data for the line that is being refreshed */
const char *line_buf = line_start(ed, ed->l_cursor_y);
size_t line_len = strcspn(line_buf, "\n");
const fx_wchar *line_buf = line_start(ed, ed->l_cursor_y);
size_t line_len = fx_wstrcspn(line_buf, L"\n");
/* the index of the first char in line_buf that needs to be reprinted */
size_t start_x = state->r_prev_cursor_x;
@@ -128,8 +129,8 @@ void backspace_nl_refresh(struct line_ed *ed, struct refresh_state *state)
/* get the data for the line that is being refreshed.
* relative to where the cursor was before the linefeed was deleted,
* this is the PREVIOUS line. */
const char *line_buf = line_start(ed, ed->l_cursor_y);
size_t line_len = strcspn(line_buf, "\n");
const fx_wchar *line_buf = line_start(ed, ed->l_cursor_y);
size_t line_len = fx_wstrcspn(line_buf, L"\n");
/* the index of the first char in line_buf that needs to be reprinted.
* subtract one to account for the linefeed that has since been deleted.
@@ -188,7 +189,7 @@ void backspace_nl_refresh(struct line_ed *ed, struct refresh_state *state)
line_buf + start_x);
line_buf += line_len + 1;
line_len = strcspn(line_buf, "\n");
line_len = fx_wstrcspn(line_buf, L"\n");
start_x = 0;
if (*line_buf == '\0') {
@@ -218,8 +219,8 @@ void backspace_nl_refresh(struct line_ed *ed, struct refresh_state *state)
void backspace_simple_refresh(struct line_ed *ed, struct refresh_state *state)
{
/* get the data for the line that is being refreshed */
const char *line_buf = line_start(ed, ed->l_cursor_y);
size_t line_len = strcspn(line_buf, "\n");
const fx_wchar *line_buf = line_start(ed, ed->l_cursor_y);
size_t line_len = fx_wstrcspn(line_buf, L"\n");
/* the index of the first char in line_buf that needs to be reprinted */
size_t start_x = ed->l_cursor_x;
+6 -1
View File
@@ -1,6 +1,7 @@
#ifndef LINE_ED_REFRESH_H_
#define LINE_ED_REFRESH_H_
#include <fx/encoding.h>
#include <stddef.h>
struct line_ed;
@@ -15,7 +16,11 @@ struct refresh_state {
size_t r_prev_line_len;
};
extern void print_text(struct line_ed *ed, size_t x, size_t y, const char *s);
extern void print_text(
struct line_ed *ed,
size_t x,
size_t y,
const fx_wchar *s);
extern void print_buffer(struct line_ed *ed);
extern void put_refresh(struct line_ed *ed, struct refresh_state *state);
extern void backspace_nl_refresh(