summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlbert Cervin <albert@acervin.com>2022-12-21 15:29:21 +0100
committerAlbert Cervin <albert@acervin.com>2022-12-21 15:29:21 +0100
commit690786504fce73edea78c7ec13b34771771e4caf (patch)
tree0a8927e6ab88bb5118eca569fdc4ae3593d7f178
parenta817e01bfe2356fdd860010d46db4e4361f343a6 (diff)
downloaddged-690786504fce73edea78c7ec13b34771771e4caf.tar.gz
dged-690786504fce73edea78c7ec13b34771771e4caf.tar.xz
dged-690786504fce73edea78c7ec13b34771771e4caf.zip
wip render rework
-rw-r--r--src/binding.h8
-rw-r--r--src/buffer.c238
-rw-r--r--src/buffer.h69
-rw-r--r--src/command.c13
-rw-r--r--src/command.h8
-rw-r--r--src/display.c148
-rw-r--r--src/display.h33
-rw-r--r--src/keyboard.c65
-rw-r--r--src/keyboard.h8
-rw-r--r--src/main.c93
-rw-r--r--src/minibuffer.c11
-rw-r--r--src/text.c123
-rw-r--r--src/text.h5
-rw-r--r--src/utf8.c49
-rw-r--r--src/utf8.h2
-rw-r--r--test/text.c23
-rw-r--r--test/utf8.c6
17 files changed, 548 insertions, 354 deletions
diff --git a/src/binding.h b/src/binding.h
index f00efed..bfde9fc 100644
--- a/src/binding.h
+++ b/src/binding.h
@@ -11,14 +11,14 @@ enum binding_type { BindingType_Command, BindingType_Keymap };
#define BINDING(mod_, c_, command_) \
(struct binding) { \
- .key = {.mod = mod_, .bytes[0] = c_, .nbytes = 1}, \
- .type = BindingType_Command, .command = hash_command_name(command_) \
+ .key = {.mod = mod_, .key = c_}, .type = BindingType_Command, \
+ .command = hash_command_name(command_) \
}
#define PREFIX(mod_, c_, keymap_) \
(struct binding) { \
- .key = {.mod = mod_, .bytes[0] = c_, .nbytes = 1}, \
- .type = BindingType_Keymap, .keymap = keymap_ \
+ .key = {.mod = mod_, .key = c_}, .type = BindingType_Keymap, \
+ .keymap = keymap_ \
}
struct binding {
diff --git a/src/buffer.c b/src/buffer.c
index 17d62af..9d9fb68 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -19,40 +19,47 @@ struct buffer buffer_create(const char *name, bool modeline) {
.text = text_create(10),
.dot_col = 0,
.dot_line = 0,
- .modeline_buf = modeline ? (uint8_t *)malloc(1024) : NULL,
.keymaps = calloc(10, sizeof(struct keymap)),
.nkeymaps = 1,
.scroll_col = 0,
.scroll_line = 0,
- .npre_update_hooks = 0,
- .pre_update_hooks = {0},
- .npost_update_hooks = 0,
- .post_update_hooks = {0},
+ .update_hooks = {0},
.nkeymaps_max = 10};
b.keymaps[0] = keymap_create("buffer-default", 128);
struct binding bindings[] = {
BINDING(Ctrl, 'B', "backward-char"),
+ BINDING(Meta, 'D', "backward-char"),
BINDING(Ctrl, 'F', "forward-char"),
+ BINDING(Meta, 'C', "forward-char"),
BINDING(Ctrl, 'P', "backward-line"),
+ BINDING(Meta, 'A', "backward-line"),
BINDING(Ctrl, 'N', "forward-line"),
+ BINDING(Meta, 'B', "forward-line"),
BINDING(Ctrl, 'A', "beginning-of-line"),
BINDING(Ctrl, 'E', "end-of-line"),
BINDING(Ctrl, 'M', "newline"),
+ BINDING(Ctrl, 'K', "kill-line"),
+ BINDING(Meta, '~', "delete-char"),
BINDING(Ctrl, '?', "backward-delete-char"),
};
keymap_bind_keys(&b.keymaps[0], bindings,
sizeof(bindings) / sizeof(bindings[0]));
+ if (modeline) {
+ struct modeline *modeline = calloc(1, sizeof(struct modeline));
+ modeline->buffer = malloc(1024);
+ buffer_add_update_hook(&b, buffer_modeline_hook, modeline);
+ }
+
return b;
}
void buffer_destroy(struct buffer *buffer) {
- free(buffer->modeline_buf);
text_destroy(buffer->text);
free(buffer->text);
}
@@ -117,6 +124,16 @@ void moveh(struct buffer *buffer, int coldelta) {
}
}
+void buffer_kill_line(struct buffer *buffer) {
+ uint32_t nchars =
+ text_line_length(buffer->text, buffer->dot_line) - buffer->dot_col;
+ if (nchars == 0) {
+ nchars = 1;
+ }
+
+ text_delete(buffer->text, buffer->dot_line, buffer->dot_col, nchars);
+}
+
void buffer_forward_delete_char(struct buffer *buffer) {
text_delete(buffer->text, buffer->dot_line, buffer->dot_col, 1);
}
@@ -185,7 +202,7 @@ void buffer_to_file(struct buffer *buffer) {
int buffer_add_text(struct buffer *buffer, uint8_t *text, uint32_t nbytes) {
uint32_t lines_added, cols_added;
- text_append_at(buffer->text, buffer->dot_line, buffer->dot_col, text, nbytes,
+ text_insert_at(buffer->text, buffer->dot_line, buffer->dot_col, text, nbytes,
&lines_added, &cols_added);
movev(buffer, lines_added);
@@ -202,78 +219,28 @@ void buffer_newline(struct buffer *buffer) {
buffer_add_text(buffer, (uint8_t *)"\n", 1);
}
-uint32_t buffer_add_pre_update_hook(struct buffer *buffer,
- pre_update_hook hook) {
- buffer->pre_update_hooks[buffer->npre_update_hooks] = hook;
- ++buffer->npre_update_hooks;
-
- return buffer->npre_update_hooks - 1;
-}
-uint32_t buffer_add_post_update_hook(struct buffer *buffer,
- post_update_hook hook) {
- buffer->post_update_hooks[buffer->npost_update_hooks] = hook;
- ++buffer->npost_update_hooks;
-
- return buffer->npost_update_hooks - 1;
-}
-
-void buffer_remove_pre_update_hook(struct buffer *buffer, uint32_t hook_id) {
- // TODO: is it needed?
-}
-
-void buffer_remove_post_update_hook(struct buffer *buffer, uint32_t hook_id) {
- // TODO: is it needed?
-}
-
-bool modeline_update(struct buffer *buffer, uint32_t width,
- uint64_t frame_time) {
- char buf[width * 4];
-
- static uint64_t samples[10] = {0};
- static uint32_t samplei = 0;
- static uint64_t avg = 0;
-
- // calc a moving average with a window of the last 10 frames
- ++samplei;
- samplei %= 10;
- avg += 0.1 * (frame_time - samples[samplei]);
- samples[samplei] = frame_time;
+uint32_t buffer_add_update_hook(struct buffer *buffer, update_hook_cb hook,
+ void *userdata) {
+ struct update_hook *h =
+ &buffer->update_hooks.hooks[buffer->update_hooks.nhooks];
+ h->callback = hook;
+ h->userdata = userdata;
- time_t now = time(NULL);
- struct tm *lt = localtime(&now);
- char left[128], right[128];
- snprintf(left, 128, " %-16s (%d, %d)", buffer->name, buffer->dot_line + 1,
- buffer->dot_col);
- snprintf(right, 128, "(%.2f ms) %02d:%02d", frame_time / 1e6, lt->tm_hour,
- lt->tm_min);
+ ++buffer->update_hooks.nhooks;
- snprintf(buf, width * 4, "\x1b[100m%s%*s%s\x1b[0m", left,
- (int)(width - (strlen(left) + strlen(right))), "", right);
- if (strcmp(buf, (char *)buffer->modeline_buf) != 0) {
- buffer->modeline_buf = realloc(buffer->modeline_buf, width * 4);
- strcpy((char *)buffer->modeline_buf, buf);
- return true;
- } else {
- return false;
- }
+ // TODO: cant really have this if someone wants to remove a hook
+ return buffer->update_hooks.nhooks - 1;
}
struct cmdbuf {
- struct render_cmd *cmds;
- uint32_t ncmds;
+ struct command_list *cmds;
uint32_t first_line;
};
void render_line(struct text_chunk *line, void *userdata) {
struct cmdbuf *cmdbuf = (struct cmdbuf *)userdata;
-
- struct render_cmd *cmd = &cmdbuf->cmds[cmdbuf->ncmds];
- cmd->col = 0;
- cmd->data = line->text;
- cmd->len = line->nbytes;
- cmd->row = line->line - cmdbuf->first_line;
-
- ++cmdbuf->ncmds;
+ command_list_draw_text(cmdbuf->cmds, 0, line->line - cmdbuf->first_line,
+ line->text, line->nbytes);
}
void scroll(struct buffer *buffer, int line_delta, int col_delta) {
@@ -296,29 +263,107 @@ void to_relative(struct buffer *buffer, uint32_t line, uint32_t col,
*rel_line = (int64_t)line - (int64_t)buffer->scroll_line;
}
+uint32_t visual_dot_col(struct buffer *buffer, uint32_t dot_col) {
+ uint32_t visual_dot_col = dot_col;
+ struct text_chunk line = text_get_line(buffer->text, buffer->dot_line);
+ for (uint32_t bytei = 0;
+ bytei <
+ text_col_to_byteindex(buffer->text, buffer->dot_line, buffer->dot_col);
+ ++bytei) {
+ if (line.text[bytei] == '\t') {
+ visual_dot_col += 3;
+ }
+ }
+
+ return visual_dot_col;
+}
+
void buffer_relative_dot_pos(struct buffer *buffer, uint32_t *relline,
uint32_t *relcol) {
int64_t rel_col, rel_line;
- to_relative(buffer, buffer->dot_line, buffer->dot_col, &rel_line, &rel_col);
+ uint32_t visual_col = visual_dot_col(buffer, buffer->dot_col);
+ to_relative(buffer, buffer->dot_line, visual_col, &rel_line, &rel_col);
*relline = rel_line < 0 ? 0 : (uint32_t)rel_line;
*relcol = rel_col < 0 ? 0 : (uint32_t)rel_col;
}
-struct buffer_update buffer_update(struct buffer *buffer, uint32_t width,
- uint32_t height, alloc_fn frame_alloc,
- uint64_t frame_time) {
+struct margin buffer_modeline_hook(struct buffer *buffer,
+ struct command_list *commands,
+ uint32_t width, uint32_t height,
+ uint64_t frame_time, void *userdata) {
+ char buf[width * 4];
+
+ static uint64_t samples[10] = {0};
+ static uint32_t samplei = 0;
+ static uint64_t avg = 0;
+
+ // calc a moving average with a window of the last 10 frames
+ ++samplei;
+ samplei %= 10;
+ avg += 0.1 * (frame_time - samples[samplei]);
+ samples[samplei] = frame_time;
+
+ time_t now = time(NULL);
+ struct tm *lt = localtime(&now);
+ char left[128], right[128];
+
+ uint32_t relcol, relline;
+ buffer_relative_dot_pos(buffer, &relline, &relcol);
+
+ snprintf(left, 128, " %-16s (%d, %d)", buffer->name, relline + 1, relcol);
+ snprintf(right, 128, "(%.2f ms) %02d:%02d", frame_time / 1e6, lt->tm_hour,
+ lt->tm_min);
+
+ snprintf(buf, width * 4, "%s%*s%s", left,
+ (int)(width - (strlen(left) + strlen(right))), "", right);
+
+ struct modeline *modeline = (struct modeline *)userdata;
+ if (strcmp(buf, (char *)modeline->buffer) != 0) {
+ modeline->buffer = realloc(modeline->buffer, width * 4);
+ strcpy((char *)modeline->buffer, buf);
+
+ command_list_set_index_color_bg(commands, 8);
+ command_list_draw_text(commands, 0, height - 1, modeline->buffer,
+ strlen((char *)modeline->buffer));
+ command_list_reset_color(commands);
+ }
+
+ struct margin m = {0};
+ m.bottom = 1;
+ return m;
+}
+
+void buffer_update(struct buffer *buffer, uint32_t width, uint32_t height,
+ struct command_list *commands, uint64_t frame_time) {
if (width == 0 || height == 0) {
- return (struct buffer_update){.cmds = NULL, .ncmds = 0};
+ return;
}
- for (uint32_t hooki = 0; hooki < buffer->npre_update_hooks; ++hooki) {
- buffer->pre_update_hooks[hooki](buffer);
+ struct margin total_margins = {0};
+ for (uint32_t hooki = 0; hooki < buffer->update_hooks.nhooks; ++hooki) {
+ struct update_hook *h = &buffer->update_hooks.hooks[hooki];
+ struct margin margins =
+ h->callback(buffer, commands, width, height, frame_time, h->userdata);
+
+ if (margins.left > total_margins.left) {
+ total_margins.left = margins.left;
+ }
+ if (margins.right > total_margins.right) {
+ total_margins.right = margins.right;
+ }
+ if (margins.top > total_margins.top) {
+ total_margins.top = margins.top;
+ }
+ if (margins.bottom > total_margins.bottom) {
+ total_margins.bottom = margins.bottom;
+ }
}
// reserve space for modeline
- uint32_t bufheight = buffer->modeline_buf != NULL ? height - 1 : height;
+ uint32_t bufheight = height - (total_margins.top + total_margins.bottom);
+ uint32_t bufwidth = width - (total_margins.left + total_margins.right);
int64_t rel_line, rel_col;
to_relative(buffer, buffer->dot_line, buffer->dot_col, &rel_line, &rel_col);
@@ -331,53 +376,22 @@ struct buffer_update buffer_update(struct buffer *buffer, uint32_t width,
if (rel_col < 0) {
col_delta = rel_col;
- } else if (rel_col > width) {
- col_delta = rel_col - width;
+ } else if (rel_col > bufwidth) {
+ col_delta = rel_col - bufwidth;
}
scroll(buffer, line_delta, col_delta);
- struct render_cmd *cmds =
- (struct render_cmd *)frame_alloc(sizeof(struct render_cmd) * (height));
-
struct cmdbuf cmdbuf = (struct cmdbuf){
- .cmds = cmds,
- .ncmds = 0,
+ .cmds = commands,
.first_line = buffer->scroll_line,
};
text_for_each_line(buffer->text, buffer->scroll_line, bufheight, render_line,
&cmdbuf);
uint32_t nlines = text_num_lines(buffer->text);
- uint32_t ncmds = cmdbuf.ncmds;
for (uint32_t linei = nlines - buffer->scroll_line; linei < bufheight;
++linei) {
- cmds[ncmds] = (struct render_cmd){
- .col = 0,
- .row = linei,
- .data = NULL,
- .len = 0,
- };
- ++ncmds;
- }
-
- if (buffer->modeline_buf != NULL &&
- modeline_update(buffer, width, frame_time)) {
- cmds[ncmds] = (struct render_cmd){
- .col = 0,
- .row = bufheight,
- .data = buffer->modeline_buf,
- .len = strlen((char *)buffer->modeline_buf),
- };
- ++ncmds;
- }
-
- for (uint32_t hooki = 0; hooki < buffer->npost_update_hooks; ++hooki) {
- buffer->post_update_hooks[hooki](buffer);
+ command_list_draw_text(commands, 0, linei, NULL, 0);
}
-
- struct buffer_update upd =
- (struct buffer_update){.cmds = cmds, .ncmds = ncmds};
-
- return upd;
}
diff --git a/src/buffer.h b/src/buffer.h
index e20e40a..141c1ea 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -7,9 +7,33 @@
struct keymap;
struct reactor;
+struct command_list;
-typedef void (*pre_update_hook)(struct buffer *);
-typedef void (*post_update_hook)(struct buffer *);
+struct margin {
+ uint32_t left;
+ uint32_t right;
+ uint32_t top;
+ uint32_t bottom;
+};
+
+typedef struct margin (*update_hook_cb)(struct buffer *buffer,
+ struct command_list *commands,
+ uint32_t width, uint32_t height,
+ uint64_t frame_time, void *userdata);
+
+struct update_hook {
+ update_hook_cb callback;
+ void *userdata;
+};
+
+struct update_hooks {
+ struct update_hook hooks[32];
+ uint32_t nhooks;
+};
+
+struct modeline {
+ uint8_t *buffer;
+};
struct buffer {
const char *name;
@@ -20,8 +44,6 @@ struct buffer {
uint32_t dot_line;
uint32_t dot_col;
- uint8_t *modeline_buf;
-
// local keymaps
struct keymap *keymaps;
uint32_t nkeymaps;
@@ -30,19 +52,9 @@ struct buffer {
uint32_t scroll_line;
uint32_t scroll_col;
- pre_update_hook pre_update_hooks[32];
- uint32_t npre_update_hooks;
- post_update_hook post_update_hooks[32];
- uint32_t npost_update_hooks;
-};
-
-struct buffer_update {
- struct render_cmd *cmds;
- uint64_t ncmds;
+ struct update_hooks update_hooks;
};
-typedef void *(alloc_fn)(size_t);
-
struct buffer buffer_create(const char *name, bool modeline);
void buffer_destroy(struct buffer *buffer);
@@ -53,6 +65,7 @@ int buffer_add_text(struct buffer *buffer, uint8_t *text, uint32_t nbytes);
void buffer_clear(struct buffer *buffer);
bool buffer_is_empty(struct buffer *buffer);
+void buffer_kill_line(struct buffer *buffer);
void buffer_forward_delete_char(struct buffer *buffer);
void buffer_backward_delete_char(struct buffer *buffer);
void buffer_backward_char(struct buffer *buffer);
@@ -66,26 +79,30 @@ void buffer_newline(struct buffer *buffer);
void buffer_relative_dot_pos(struct buffer *buffer, uint32_t *relline,
uint32_t *relcol);
-uint32_t buffer_add_pre_update_hook(struct buffer *buffer,
- pre_update_hook hook);
-uint32_t buffer_add_post_update_hook(struct buffer *buffer,
- post_update_hook hook);
-void buffer_remove_pre_update_hook(struct buffer *buffer, uint32_t hook_id);
-void buffer_remove_post_update_hook(struct buffer *buffer, uint32_t hook_id);
+uint32_t buffer_add_update_hook(struct buffer *buffer, update_hook_cb hook,
+ void *userdata);
struct buffer buffer_from_file(const char *filename, struct reactor *reactor);
void buffer_to_file(struct buffer *buffer);
-struct buffer_update buffer_update(struct buffer *buffer, uint32_t width,
- uint32_t height, alloc_fn frame_alloc,
- uint64_t frame_time);
+void buffer_update(struct buffer *buffer, uint32_t width, uint32_t height,
+ struct command_list *commands, uint64_t frame_time);
+
+struct margin buffer_modeline_hook(struct buffer *buffer,
+ struct command_list *commands,
+ uint32_t width, uint32_t height,
+ uint64_t frame_time, void *userdata);
// commands
#define BUFFER_WRAPCMD(fn) \
- static void fn##_cmd(struct command_ctx ctx, int argc, const char *argv[]) { \
+ static int32_t fn##_cmd(struct command_ctx ctx, int argc, \
+ const char *argv[]) { \
fn(ctx.current_buffer); \
+ return 0; \
}
+BUFFER_WRAPCMD(buffer_kill_line);
+BUFFER_WRAPCMD(buffer_forward_delete_char);
BUFFER_WRAPCMD(buffer_backward_delete_char);
BUFFER_WRAPCMD(buffer_backward_char);
BUFFER_WRAPCMD(buffer_forward_char);
@@ -97,6 +114,8 @@ BUFFER_WRAPCMD(buffer_newline)
BUFFER_WRAPCMD(buffer_to_file);
static struct command BUFFER_COMMANDS[] = {
+ {.name = "kill-line", .fn = buffer_kill_line_cmd},
+ {.name = "delete-char", .fn = buffer_forward_delete_char_cmd},
{.name = "backward-delete-char", .fn = buffer_backward_delete_char_cmd},
{.name = "backward-char", .fn = buffer_backward_char_cmd},
{.name = "forward-char", .fn = buffer_forward_char_cmd},
diff --git a/src/command.c b/src/command.c
index fcf53e2..a667750 100644
--- a/src/command.c
+++ b/src/command.c
@@ -2,7 +2,7 @@
#include <stdlib.h>
-struct commands command_list_create(uint32_t capacity) {
+struct commands command_registry_create(uint32_t capacity) {
return (struct commands){
.commands = calloc(capacity, sizeof(struct hashed_command)),
.ncommands = 0,
@@ -10,7 +10,7 @@ struct commands command_list_create(uint32_t capacity) {
};
}
-void command_list_destroy(struct commands *commands) {
+void command_registry_destroy(struct commands *commands) {
free(commands->commands);
commands->ncommands = 0;
commands->capacity = 0;
@@ -68,10 +68,7 @@ struct command *lookup_command_by_hash(struct commands *commands,
int32_t execute_command(struct command *command, struct buffer *current_buffer,
int argc, const char *argv[]) {
- command->fn((struct command_ctx){.current_buffer = current_buffer,
- .userdata = command->userdata},
- argc, argv);
-
- // TODO
- return 0;
+ return command->fn((struct command_ctx){.current_buffer = current_buffer,
+ .userdata = command->userdata},
+ argc, argv);
}
diff --git a/src/command.h b/src/command.h
index b02c74a..fe998ae 100644
--- a/src/command.h
+++ b/src/command.h
@@ -7,8 +7,8 @@ struct command_ctx {
void *userdata;
};
-typedef void (*command_fn)(struct command_ctx ctx, int argc,
- const char *argv[]);
+typedef int32_t (*command_fn)(struct command_ctx ctx, int argc,
+ const char *argv[]);
struct command {
const char *name;
@@ -27,8 +27,8 @@ struct commands {
uint32_t capacity;
};
-struct commands command_list_create(uint32_t capacity);
-void command_list_destroy(struct commands *commands);
+struct commands command_registry_create(uint32_t capacity);
+void command_registry_destroy(struct commands *commands);
uint32_t register_command(struct commands *commands, struct command *command);
void register_commands(struct commands *command_list, struct command *commands,
diff --git a/src/display.c b/src/display.c
index b382ea1..94b1c36 100644
--- a/src/display.c
+++ b/src/display.c
@@ -3,6 +3,7 @@
#include "buffer.h"
+#include <stdarg.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <unistd.h>
@@ -47,7 +48,15 @@ void display_destroy(struct display *display) {
}
void putbytes(uint8_t *line_bytes, uint32_t line_length) {
- fwrite(line_bytes, 1, line_length, stdout);
+ for (uint32_t bytei = 0; bytei < line_length; ++bytei) {
+ uint8_t byte = line_bytes[bytei];
+
+ if (byte == '\t') {
+ fputs(" ", stdout);
+ } else {
+ fputc(byte, stdout);
+ }
+ }
}
void putbyte(uint8_t c) { putc(c, stdout); }
@@ -84,23 +93,130 @@ void delete_to_eol() {
putbytes(bytes, 3);
}
-void display_update(struct display *display, struct render_cmd_buf *cmd_bufs,
- uint32_t ncmd_bufs, uint32_t currow, uint32_t curcol) {
- for (uint32_t bufi = 0; bufi < ncmd_bufs; ++bufi) {
- struct render_cmd_buf *buf = &cmd_bufs[bufi];
- uint64_t ncmds = buf->ncmds;
- struct render_cmd *cmds = buf->cmds;
+struct command_list *command_list_create(uint32_t capacity, alloc_fn allocator,
+ uint32_t xoffset, uint32_t yoffset) {
+ struct command_list *command_list = allocator(sizeof(struct command_list));
- for (uint64_t cmdi = 0; cmdi < ncmds; ++cmdi) {
- struct render_cmd *cmd = &cmds[cmdi];
- display_move_cursor(display, cmd->row + buf->yoffset,
- cmd->col + buf->xoffset);
- putbytes(cmd->data, cmd->len);
- delete_to_eol();
- }
+ command_list->capacity = capacity;
+ command_list->ncmds = 0;
+ command_list->xoffset = xoffset;
+ command_list->yoffset = yoffset;
+ command_list->format_len = 0;
+
+ command_list->cmds = allocator(sizeof(struct render_command) * capacity);
+
+ return command_list;
+}
+
+void push_format(struct command_list *list, const char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ if (list->format_len == 0) {
+ list->format[0] = ESC;
+ list->format[1] = '[';
+
+ list->format_len = 2;
+ }
+
+ if (list->format_len > 2) {
+ list->format[list->format_len] = ';';
+ ++list->format_len;
+ }
+
+ uint32_t format_space_left = sizeof(list->format) - 1 - list->format_len;
+ list->format_len += vsnprintf((char *)(list->format + list->format_len),
+ format_space_left, fmt, args);
+
+ va_end(args);
+}
+
+void flush_format(struct command_list *list, uint32_t col, uint32_t row) {
+ list->format[list->format_len] = 'm';
+ ++list->format_len;
+
+ struct render_command *cmd = &list->cmds[list->ncmds];
+ cmd->data = list->format;
+ cmd->col = col + list->xoffset;
+ cmd->row = row + list->yoffset;
+ cmd->len = list->format_len;
+
+ list->format_len = 0;
+
+ ++list->ncmds;
+}
+
+void command_list_draw_text(struct command_list *list, uint32_t col,
+ uint32_t row, uint8_t *data, uint32_t len) {
+ uint32_t needed_capacity = list->ncmds + 1;
+ if (list->format_len > 0) {
+ ++needed_capacity;
}
- display_move_cursor(display, currow, curcol);
+ if (needed_capacity > list->capacity) {
+ // TODO: better
+ return;
+ }
+
+ if (list->format_len > 0) {
+ flush_format(list, col, row);
+ }
+
+ struct render_command *cmd = &list->cmds[list->ncmds];
+ cmd->data = data;
+ cmd->col = col + list->xoffset;
+ cmd->row = row + list->yoffset;
+ cmd->len = len;
+
+ ++list->ncmds;
+}
- fflush(stdout);
+void command_list_set_index_color_fg(struct command_list *list,
+ uint8_t color_idx) {
+ if (color_idx < 8) {
+ push_format(list, "%d", 30 + color_idx);
+ } else if (color_idx < 16) {
+ push_format(list, "%d", 90 + color_idx);
+ } else {
+ push_format(list, "30;5;%d", color_idx);
+ }
+}
+void command_list_set_color_fg(struct command_list *list, uint8_t red,
+ uint8_t green, uint8_t blue) {
+ push_format(list, "30;2;%d;%d;%d", red, green, blue);
+}
+
+void command_list_set_index_color_bg(struct command_list *list,
+ uint8_t color_idx) {
+ if (color_idx < 8) {
+ push_format(list, "%d", 40 + color_idx);
+ } else if (color_idx < 16) {
+ push_format(list, "%d", 100 + color_idx);
+ } else {
+ push_format(list, "40;5;%d", color_idx);
+ }
}
+
+void command_list_set_color_bg(struct command_list *list, uint8_t red,
+ uint8_t green, uint8_t blue) {
+ push_format(list, "40;2;%d;%d;%d", red, green, blue);
+}
+
+void command_list_reset_color(struct command_list *list) {
+ push_format(list, "0");
+}
+
+void display_render(struct display *display,
+ struct command_list *command_list) {
+
+ struct command_list *cl = command_list;
+
+ for (uint64_t cmdi = 0; cmdi < cl->ncmds; ++cmdi) {
+ struct render_command *cmd = &cl->cmds[cmdi];
+ display_move_cursor(display, cmd->row, cmd->col);
+ putbytes(cmd->data, cmd->len);
+ delete_to_eol();
+ }
+}
+
+void display_begin_render(struct display *display) {}
+void display_end_render(struct display *display) { fflush(stdout); }
diff --git a/src/display.h b/src/display.h
index 088a487..c1c3667 100644
--- a/src/display.h
+++ b/src/display.h
@@ -1,3 +1,4 @@
+#include <stddef.h>
#include <stdint.h>
#include <termios.h>
@@ -9,7 +10,7 @@ struct display {
uint32_t height;
};
-struct render_cmd {
+struct render_command {
uint32_t col;
uint32_t row;
@@ -17,11 +18,15 @@ struct render_cmd {
uint32_t len;
};
-struct render_cmd_buf {
- struct render_cmd *cmds;
+struct command_list {
+ struct render_command *cmds;
uint64_t ncmds;
+ uint64_t capacity;
uint32_t xoffset;
uint32_t yoffset;
+
+ uint8_t format[64];
+ uint32_t format_len;
};
struct display display_create();
@@ -29,5 +34,23 @@ void display_destroy(struct display *display);
void display_clear(struct display *display);
void display_move_cursor(struct display *display, uint32_t row, uint32_t col);
-void display_update(struct display *display, struct render_cmd_buf *cmd_bufs,
- uint32_t ncmd_bufs, uint32_t currow, uint32_t curcol);
+
+void display_begin_render(struct display *display);
+void display_render(struct display *display, struct command_list *command_list);
+void display_end_render(struct display *display);
+
+typedef void *(*alloc_fn)(size_t);
+struct command_list *command_list_create(uint32_t capacity, alloc_fn allocator,
+ uint32_t xoffset, uint32_t yoffset);
+
+void command_list_set_index_color_bg(struct command_list *list,
+ uint8_t color_idx);
+void command_list_set_color_bg(struct command_list *list, uint8_t red,
+ uint8_t green, uint8_t blue);
+void command_list_set_index_color_fg(struct command_list *list,
+ uint8_t color_idx);
+void command_list_set_color_fg(struct command_list *list, uint8_t red,
+ uint8_t green, uint8_t blue);
+void command_list_reset_color(struct command_list *list);
+void command_list_draw_text(struct command_list *list, uint32_t col,
+ uint32_t row, uint8_t *data, uint32_t len);
diff --git a/src/keyboard.c b/src/keyboard.c
index b4c0c6d..ae74e8e 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -26,33 +26,28 @@ void parse_keys(uint8_t *bytes, uint32_t nbytes, struct key *out_keys,
uint8_t b = bytes[bytei];
struct key *kp = &out_keys[nkps];
- kp->nbytes = 1;
+ kp->start = bytei;
+ bool inserted = true;
if (b == 0x1b) { // meta
kp->mod |= Meta;
} else if (b >= 0x00 && b <= 0x1f) { // ctrl char
kp->mod |= Ctrl;
- kp->bytes[0] = b | 0x40;
- ++nkps;
- prevkp = kp;
+ kp->key = b | 0x40;
} else if (b == 0x7f) { // ^?
kp->mod |= Ctrl;
- kp->bytes[0] = '?';
- ++nkps;
- prevkp = kp;
- } else if (utf8_byte_is_unicode_start((uint8_t)b)) {
- kp->bytes[0] = b;
- ++nkps;
- prevkp = kp;
- } else if (utf8_byte_is_unicode_continuation((uint8_t)b)) {
- prevkp->bytes[prevkp->nbytes] = b;
- ++prevkp->nbytes;
- } else { /* ascii char */
- if (prevkp->mod & Meta) {
- prevkp->bytes[0] = b;
- } else {
- kp->bytes[0] = b;
- }
+ kp->key = '?';
+ } else if (prevkp->mod & Meta) {
+ prevkp->key = b;
+ prevkp->end = bytei + 1;
+ inserted = false;
+ } else {
+ inserted = false;
+ }
+
+ kp->end = bytei + 1;
+
+ if (inserted) {
++nkps;
prevkp = kp;
}
@@ -64,8 +59,12 @@ void parse_keys(uint8_t *bytes, uint32_t nbytes, struct key *out_keys,
struct keyboard_update keyboard_update(struct keyboard *kbd,
struct reactor *reactor) {
- struct keyboard_update upd =
- (struct keyboard_update){.keys = {0}, .nkeys = 0};
+ struct keyboard_update upd = (struct keyboard_update){
+ .keys = {0},
+ .nkeys = 0,
+ .nbytes = 0,
+ .raw = {0},
+ };
if (!kbd->has_data) {
if (reactor_poll_event(reactor, kbd->reactor_event_id)) {
@@ -75,11 +74,15 @@ struct keyboard_update keyboard_update(struct keyboard *kbd,
}
}
- uint8_t bytes[32] = {0};
- int nbytes = read(STDIN_FILENO, bytes, 32);
+ int nbytes = read(STDIN_FILENO, upd.raw, 32);
if (nbytes > 0) {
- parse_keys(bytes, nbytes, upd.keys, &upd.nkeys, &kbd->last_key);
+ upd.nbytes = nbytes;
+ parse_keys(upd.raw, upd.nbytes, upd.keys, &upd.nkeys, &kbd->last_key);
+
+ if (nbytes < 32) {
+ kbd->has_data = false;
+ }
} else if (nbytes == EAGAIN) {
kbd->has_data = false;
}
@@ -88,12 +91,11 @@ struct keyboard_update keyboard_update(struct keyboard *kbd,
}
bool key_equal_char(struct key *key, uint8_t mod, uint8_t c) {
- return key->bytes[0] == c && key->mod == mod;
+ return key->key == c && key->mod == mod;
}
bool key_equal(struct key *key1, struct key *key2) {
- return memcmp(key1->bytes, key2->bytes, key1->nbytes) == 0 &&
- key1->mod == key2->mod && key1->nbytes == key2->nbytes;
+ return key1->key == key2->key && key1->mod == key2->mod;
}
void key_name(struct key *key, char *buf, size_t capacity) {
@@ -107,10 +109,5 @@ void key_name(struct key *key, char *buf, size_t capacity) {
break;
}
- uint8_t lower[6];
- for (uint32_t bytei = 0; bytei < key->nbytes; ++bytei) {
- lower[bytei] = tolower(key->bytes[bytei]);
- }
-
- snprintf(buf, capacity, "%s%.*s", mod, key->nbytes, lower);
+ snprintf(buf, capacity, "%s%c", mod, tolower(key->key));
}
diff --git a/src/keyboard.h b/src/keyboard.h
index 18630c3..b7f1940 100644
--- a/src/keyboard.h
+++ b/src/keyboard.h
@@ -8,9 +8,10 @@ enum modifiers {
};
struct key {
- uint8_t bytes[6];
- uint8_t nbytes;
+ uint8_t key;
uint8_t mod;
+ uint8_t start;
+ uint8_t end;
};
struct keyboard {
@@ -22,6 +23,9 @@ struct keyboard {
struct keyboard_update {
struct key keys[32];
uint32_t nkeys;
+
+ uint8_t raw[32];
+ uint32_t nbytes;
};
struct reactor;
diff --git a/src/main.c b/src/main.c
index dc7cf63..ed5417a 100644
--- a/src/main.c
+++ b/src/main.c
@@ -46,16 +46,20 @@ bool running = true;
void terminate() { running = false; }
-void _abort(struct command_ctx ctx, int argc, const char *argv[]) {
+int32_t _abort(struct command_ctx ctx, int argc, const char *argv[]) {
minibuffer_echo_timeout(4, "๐Ÿ’ฃ aborted");
+ return 0;
}
-void unimplemented_command(struct command_ctx ctx, int argc,
- const char *argv[]) {
+int32_t unimplemented_command(struct command_ctx ctx, int argc,
+ const char *argv[]) {
minibuffer_echo("TODO: %s is not implemented", (const char *)ctx.userdata);
+ return 0;
}
-void exit_editor(struct command_ctx ctx, int argc, const char *argv[]) {
+
+int32_t exit_editor(struct command_ctx ctx, int argc, const char *argv[]) {
terminate();
+ return 0;
}
static struct command GLOBAL_COMMANDS[] = {
@@ -100,10 +104,10 @@ struct window {
struct buffer *buffer;
};
-struct buffer_update window_update_buffer(struct window *window,
- uint64_t frame_time) {
- return buffer_update(window->buffer, window->width, window->height,
- frame_alloc, frame_time);
+void window_update_buffer(struct window *window, struct command_list *commands,
+ uint64_t frame_time) {
+ buffer_update(window->buffer, window->width, window->height, commands,
+ frame_time);
}
void buffers_init(struct buffers *buffers) { buffers->nbuffers = 0; }
@@ -131,7 +135,7 @@ int main(int argc, char *argv[]) {
signal(SIGTERM, terminate);
- frame_allocator = frame_allocator_create(1024 * 1024);
+ frame_allocator = frame_allocator_create(16 * 1024 * 1024);
// create reactor
struct reactor reactor = reactor_create();
@@ -144,7 +148,7 @@ int main(int argc, char *argv[]) {
struct keyboard kbd = keyboard_create(&reactor);
// commands
- struct commands commands = command_list_create(32);
+ struct commands commands = command_registry_create(32);
register_commands(&commands, GLOBAL_COMMANDS,
sizeof(GLOBAL_COMMANDS) / sizeof(GLOBAL_COMMANDS[0]));
register_commands(&commands, BUFFER_COMMANDS,
@@ -171,19 +175,20 @@ int main(int argc, char *argv[]) {
struct buffers buflist = {0};
buffers_init(&buflist);
- struct buffer curbuf = buffer_create("welcome", true);
+ struct buffer initial_buffer = buffer_create("welcome", true);
if (filename != NULL) {
- curbuf = buffer_from_file(filename, &reactor);
+ initial_buffer = buffer_from_file(filename, &reactor);
} else {
const char *welcome_txt = "Welcome to the editor for datagubbar ๐Ÿ‘ด\n";
- buffer_add_text(&curbuf, (uint8_t *)welcome_txt, strlen(welcome_txt));
+ buffer_add_text(&initial_buffer, (uint8_t *)welcome_txt,
+ strlen(welcome_txt));
}
- buffers_add(&buflist, curbuf);
+ buffers_add(&buflist, initial_buffer);
// one main window
struct window main_window = (struct window){
- .buffer = &curbuf,
+ .buffer = &initial_buffer,
.height = display.height - 1,
.width = display.width,
.x = 0,
@@ -208,13 +213,13 @@ int main(int argc, char *argv[]) {
uint64_t frame_time = 0;
- struct render_cmd_buf render_bufs[2] = {0};
-
struct window *windows[2] = {
&minibuffer_window,
&main_window,
};
+ struct command_list *command_lists[2] = {0};
+
// TODO: not always
struct window *active_window = &main_window;
@@ -224,13 +229,13 @@ int main(int argc, char *argv[]) {
// update windows
uint32_t dot_line = 0, dot_col = 0;
- for (uint32_t windowi = 0; windowi < 2; ++windowi) {
+ for (uint32_t windowi = 0; windowi < sizeof(windows) / sizeof(windows[0]);
+ ++windowi) {
struct window *win = windows[windowi];
- struct buffer_update buf_upd = window_update_buffer(win, frame_time);
- render_bufs[windowi].cmds = buf_upd.cmds;
- render_bufs[windowi].ncmds = buf_upd.ncmds;
- render_bufs[windowi].xoffset = win->x;
- render_bufs[windowi].yoffset = win->y;
+ // TODO: better capacity
+ command_lists[windowi] =
+ command_list_create(win->height * 2, frame_alloc, win->x, win->y);
+ window_update_buffer(win, command_lists[windowi], frame_time);
}
clock_gettime(CLOCK_MONOTONIC, &buffer_end);
@@ -239,22 +244,36 @@ int main(int argc, char *argv[]) {
clock_gettime(CLOCK_MONOTONIC, &display_begin);
uint32_t relline, relcol;
buffer_relative_dot_pos(active_window->buffer, &relline, &relcol);
- if (render_bufs[0].ncmds > 0 || render_bufs[1].ncmds > 0) {
- display_update(&display, render_bufs, 2, relline, relcol);
- }
+ display_begin_render(&display);
+ for (uint32_t windowi = 0; windowi < sizeof(windows) / sizeof(windows[0]);
+ ++windowi) {
+ display_render(&display, command_lists[windowi]);
+ }
+ display_move_cursor(&display, relline + active_window->y,
+ relcol + active_window->x);
+ display_end_render(&display);
clock_gettime(CLOCK_MONOTONIC, &display_end);
+ // this blocks for events, so if nothing has happened we block here.
reactor_update(&reactor);
clock_gettime(CLOCK_MONOTONIC, &keyboard_begin);
struct keymap *local_keymaps = NULL;
- uint32_t nbuffer_keymaps = buffer_keymaps(&curbuf, &local_keymaps);
+ uint32_t nbuffer_keymaps = buffer_keymaps(&initial_buffer, &local_keymaps);
struct keyboard_update kbd_upd = keyboard_update(&kbd, &reactor);
+ uint32_t input_data_idx = 0;
for (uint32_t ki = 0; ki < kbd_upd.nkeys; ++ki) {
struct key *k = &kbd_upd.keys[ki];
+ // insert any data from last key
+ if (k->start > input_data_idx) {
+ buffer_add_text(active_window->buffer, &kbd_upd.raw[input_data_idx],
+ k->start - input_data_idx);
+ }
+ input_data_idx = k->end;
+
struct lookup_result res = {.found = false};
if (current_keymap != NULL) {
res = lookup_key(current_keymap, 1, k, &commands);
@@ -269,7 +288,17 @@ int main(int argc, char *argv[]) {
if (res.found) {
switch (res.type) {
case BindingType_Command: {
- execute_command(res.command, active_window->buffer, 0, NULL);
+ if (res.command == NULL) {
+ minibuffer_echo_timeout(
+ 4, "binding found for key %s but not command", k);
+ } else {
+ int32_t ec =
+ execute_command(res.command, active_window->buffer, 0, NULL);
+ if (ec != 0) {
+ minibuffer_echo_timeout(4, "command %s failed with exit code %d",
+ res.command->name, ec);
+ }
+ }
current_keymap = NULL;
break;
}
@@ -284,10 +313,12 @@ int main(int argc, char *argv[]) {
} else if (current_keymap != NULL) {
minibuffer_echo_timeout(4, "key is not bound!");
current_keymap = NULL;
- } else {
- buffer_add_text(active_window->buffer, k->bytes, k->nbytes);
}
}
+ if (input_data_idx < kbd_upd.nbytes) {
+ buffer_add_text(active_window->buffer, &kbd_upd.raw[input_data_idx],
+ kbd_upd.nbytes - input_data_idx);
+ }
clock_gettime(CLOCK_MONOTONIC, &keyboard_end);
// calculate frame time
@@ -302,7 +333,7 @@ int main(int argc, char *argv[]) {
display_clear(&display);
display_destroy(&display);
keymap_destroy(&global_keymap);
- command_list_destroy(&commands);
+ command_registry_destroy(&commands);
return 0;
}
diff --git a/src/minibuffer.c b/src/minibuffer.c
index 649413b..762bbe7 100644
--- a/src/minibuffer.c
+++ b/src/minibuffer.c
@@ -11,17 +11,22 @@ static struct minibuffer {
struct timespec expires;
} g_minibuffer = {0};
-void update(struct buffer *buffer) {
+struct margin update(struct buffer *buffer, struct command_list *commands,
+ uint32_t width, uint32_t height, uint64_t frame_time,
+ void *userdata) {
struct timespec current;
+ struct minibuffer *mb = (struct minibuffer *)userdata;
clock_gettime(CLOCK_MONOTONIC, &current);
- if (current.tv_sec >= g_minibuffer.expires.tv_sec) {
+ if (current.tv_sec >= mb->expires.tv_sec) {
buffer_clear(buffer);
}
+
+ return (struct margin){0};
}
void minibuffer_init(struct buffer *buffer) {
g_minibuffer.buffer = buffer;
- buffer_add_pre_update_hook(g_minibuffer.buffer, update);
+ buffer_add_update_hook(g_minibuffer.buffer, update, &g_minibuffer);
}
void echo(uint32_t timeout, const char *fmt, va_list args) {
diff --git a/src/text.c b/src/text.c
index 4ab10b1..d1a0db3 100644
--- a/src/text.c
+++ b/src/text.c
@@ -64,10 +64,13 @@ uint32_t charidx_to_byteidx(struct line *line, uint32_t char_idx) {
if (char_idx > line->nchars) {
return line->nbytes;
}
- return utf8_nbytes(line->data, char_idx);
+ return utf8_nbytes(line->data, line->nbytes, char_idx);
+}
+
+uint32_t text_col_to_byteindex(struct text *text, uint32_t line, uint32_t col) {
+ return charidx_to_byteidx(&text->lines[line], col);
}
-// TODO: grapheme clusters
// given `byte_idx` as a byte index, return the character index
uint32_t byteidx_to_charidx(struct line *line, uint32_t byte_idx) {
if (byte_idx > line->nbytes) {
@@ -77,31 +80,6 @@ uint32_t byteidx_to_charidx(struct line *line, uint32_t byte_idx) {
return utf8_nchars(line->data, byte_idx);
}
-uint32_t char_byte_size(struct line *line, uint32_t byte_idx) {
- return utf8_nbytes(line->data + byte_idx, 1);
-}
-
-void extend_line(struct line *line, uint32_t nbytes, uint32_t nchars) {
- if (nbytes == 0) {
- return;
- }
-
- line->nbytes += nbytes;
- line->nchars += nchars;
- line->flags = LineChanged;
- line->data = realloc(line->data, line->nbytes);
-}
-
-void insert_at(struct line *line, uint32_t byte_index, uint8_t *text,
- uint32_t len, uint32_t nchars) {
- if (len == 0) {
- return;
- }
-
- extend_line(line, len, nchars);
- memcpy(line->data + byte_index, text, len);
-}
-
void insert_at_col(struct line *line, uint32_t col, uint8_t *text, uint32_t len,
uint32_t nchars) {
@@ -109,14 +87,17 @@ void insert_at_col(struct line *line, uint32_t col, uint8_t *text, uint32_t len,
return;
}
- extend_line(line, len, nchars);
+ line->nbytes += len;
+ line->nchars += nchars;
+ line->flags = LineChanged;
+ line->data = realloc(line->data, line->nbytes);
uint32_t bytei = charidx_to_byteidx(line, col);
- // move chars out of the way
- if (col + nchars < line->nchars) {
- uint32_t nextcbytei = charidx_to_byteidx(line, col + nchars);
- memmove(line->data + nextcbytei, line->data + bytei,
- line->nbytes - nextcbytei);
+
+ // move following bytes out of the way
+ if (bytei + len < line->nbytes) {
+ uint32_t start = bytei + len;
+ memmove(line->data + start, line->data + bytei, line->nbytes - start);
}
// insert new chars
@@ -228,91 +209,61 @@ void delete_line(struct text *text, uint32_t line) {
void text_append(struct text *text, uint8_t *bytes, uint32_t nbytes,
uint32_t *lines_added, uint32_t *cols_added) {
-
- uint32_t linelen = 0, nchars_counted = 0, nlines_added = 0, ncols_added = 0;
uint32_t line = text->nlines - 1;
+ uint32_t col = text_line_length(text, line);
- for (uint32_t bytei = 0; bytei < nbytes; ++bytei) {
- uint8_t byte = bytes[bytei];
- if (byte == '\n') {
- insert_at(&text->lines[line], text->lines[line].nbytes,
- bytes + (bytei - linelen), linelen, nchars_counted);
-
- new_line_at(text, line, text->lines[line].nchars);
-
- ++line;
- ++nlines_added;
-
- linelen = 0;
- nchars_counted = 0;
- } else {
- if (utf8_byte_is_ascii(byte) || utf8_byte_is_unicode_start(byte)) {
- ++nchars_counted;
- }
- ++linelen;
- }
- }
-
- // handle remaining
- if (linelen > 0) {
- insert_at(&text->lines[line], text->lines[line].nbytes,
- bytes + (nbytes - linelen), linelen, nchars_counted);
- ncols_added = nchars_counted;
- }
-
- *lines_added = nlines_added;
- *cols_added = ncols_added;
+ text_insert_at(text, line, col, bytes, nbytes, lines_added, cols_added);
}
-void text_append_at(struct text *text, uint32_t line, uint32_t col,
+void text_insert_at(struct text *text, uint32_t line, uint32_t col,
uint8_t *bytes, uint32_t nbytes, uint32_t *lines_added,
uint32_t *cols_added) {
- uint32_t linelen = 0;
- uint32_t nchars_counted = 0;
- uint32_t nlines_added = 0;
- uint32_t ncols_added = 0;
+ uint32_t linelen = 0, start_line = line;
+ *cols_added = 0;
+
for (uint32_t bytei = 0; bytei < nbytes; ++bytei) {
uint8_t byte = bytes[bytei];
if (byte == '\n') {
- insert_at_col(&text->lines[line], col, bytes + (bytei - linelen), linelen,
- nchars_counted);
+ uint8_t *line_data = bytes + (bytei - linelen);
+ uint32_t nchars = utf8_nchars(line_data, linelen);
+ insert_at_col(&text->lines[line], col, line_data, linelen, nchars);
- col += nchars_counted;
+ col += nchars;
new_line_at(text, line, col);
++line;
- ++nlines_added;
col = text_line_length(text, line);
linelen = 0;
- nchars_counted = 0;
} else {
- if (utf8_byte_is_ascii(byte) || utf8_byte_is_unicode_start(byte)) {
- ++nchars_counted;
- }
++linelen;
}
}
// handle remaining
if (linelen > 0) {
- insert_at_col(&text->lines[line], col, bytes + (nbytes - linelen), linelen,
- nchars_counted);
- ncols_added = nchars_counted;
+ uint8_t *line_data = bytes + (nbytes - linelen);
+ uint32_t nchars = utf8_nchars(line_data, linelen);
+ insert_at_col(&text->lines[line], col, line_data, linelen, nchars);
+ *cols_added = nchars;
}
- *lines_added = nlines_added;
- *cols_added = ncols_added;
+ *lines_added = line - start_line;
}
void text_delete(struct text *text, uint32_t line, uint32_t col,
uint32_t nchars) {
- // delete chars from current line
struct line *lp = &text->lines[line];
+ if (col > lp->nchars) {
+ return;
+ }
+
+ // delete chars from current line
uint32_t chars_initial_line =
col + nchars > lp->nchars ? (lp->nchars - col) : nchars;
uint32_t bytei = charidx_to_byteidx(lp, col);
- uint32_t nbytes = utf8_nbytes(lp->data + bytei, chars_initial_line);
+ uint32_t nbytes =
+ utf8_nbytes(lp->data + bytei, lp->nbytes - bytei, chars_initial_line);
memcpy(lp->data + bytei, lp->data + bytei + nbytes,
lp->nbytes - (bytei + nbytes));
@@ -347,7 +298,7 @@ void text_delete(struct text *text, uint32_t line, uint32_t col,
}
// delete all lines from current line + 1 to (and including) last line
- for (uint32_t li = initial_line + 1; li <= line; ++li) {
+ for (uint32_t li = initial_line + 1; li <= line && li < text->nlines; ++li) {
delete_line(text, li);
}
}
diff --git a/src/text.h b/src/text.h
index 31fc9ca..213cf9e 100644
--- a/src/text.h
+++ b/src/text.h
@@ -5,7 +5,7 @@
// opaque so it is easier to change representation to gap, rope etc.
struct text;
-struct render_cmd;
+struct render_command;
struct text *text_create(uint32_t initial_capacity);
void text_destroy(struct text *text);
@@ -15,7 +15,7 @@ void text_destroy(struct text *text);
*/
void text_clear(struct text *text);
-void text_append_at(struct text *text, uint32_t line, uint32_t col,
+void text_insert_at(struct text *text, uint32_t line, uint32_t col,
uint8_t *bytes, uint32_t nbytes, uint32_t *lines_added,
uint32_t *cols_added);
@@ -28,6 +28,7 @@ void text_delete(struct text *text, uint32_t line, uint32_t col,
uint32_t text_num_lines(struct text *text);
uint32_t text_line_length(struct text *text, uint32_t lineidx);
uint32_t text_line_size(struct text *text, uint32_t lineidx);
+uint32_t text_col_to_byteindex(struct text *text, uint32_t line, uint32_t col);
struct text_chunk {
uint8_t *text;
diff --git a/src/utf8.c b/src/utf8.c
index 3afef40..abf5ef7 100644
--- a/src/utf8.c
+++ b/src/utf8.c
@@ -9,33 +9,58 @@ bool utf8_byte_is_unicode_continuation(uint8_t byte) {
bool utf8_byte_is_unicode(uint8_t byte) { return (byte & 0x80) != 0x0; }
bool utf8_byte_is_ascii(uint8_t byte) { return !utf8_byte_is_unicode(byte); }
+uint32_t utf8_nbytes_in_char(uint8_t byte) {
+ // length of char is the number of leading ones
+ // flip it and count number of leading zeros
+ uint8_t invb = ~byte;
+ return __builtin_clz((uint32_t)invb) - 24;
+}
+
// TODO: grapheme clusters, this returns the number of unicode code points
uint32_t utf8_nchars(uint8_t *bytes, uint32_t nbytes) {
uint32_t nchars = 0;
+ uint32_t expected = 0;
for (uint32_t bi = 0; bi < nbytes; ++bi) {
- if (utf8_byte_is_ascii(bytes[bi]) || utf8_byte_is_unicode_start(bytes[bi]))
+ uint8_t byte = bytes[bi];
+ if (utf8_byte_is_unicode(byte)) {
+ if (utf8_byte_is_unicode_start(byte)) {
+ expected = utf8_nbytes_in_char(byte) - 1;
+ } else { // continuation byte
+ --expected;
+ if (expected == 0) {
+ ++nchars;
+ }
+ }
+ } else { // ascii
++nchars;
+ }
}
return nchars;
}
// TODO: grapheme clusters, this uses the number of unicode code points
-uint32_t utf8_nbytes(uint8_t *bytes, uint32_t nchars) {
+uint32_t utf8_nbytes(uint8_t *bytes, uint32_t nbytes, uint32_t nchars) {
+
uint32_t bi = 0;
uint32_t chars = 0;
- while (chars < nchars) {
- uint8_t byte = bytes[bi];
- if (utf8_byte_is_unicode_start(byte)) {
- ++chars;
+ uint32_t expected = 0;
- // length of char is the number of leading ones
- // flip it and count number of leading zeros
- uint8_t invb = ~byte;
- bi += __builtin_clz((uint32_t)invb) - 24;
- } else {
+ while (chars < nchars && bi < nbytes) {
+ uint8_t byte = bytes[bi];
+ if (utf8_byte_is_unicode(byte)) {
+ if (utf8_byte_is_unicode_start(byte)) {
+ expected = utf8_nbytes_in_char(byte) - 1;
+ } else { // continuation char
+ --expected;
+ if (expected == 0) {
+ ++chars;
+ }
+ }
+ } else { // ascii
++chars;
- ++bi;
}
+
+ ++bi;
}
return bi;
diff --git a/src/utf8.h b/src/utf8.h
index 901b1af..59a959e 100644
--- a/src/utf8.h
+++ b/src/utf8.h
@@ -8,7 +8,7 @@
uint32_t utf8_nchars(uint8_t *bytes, uint32_t nbytes);
/* Return the number of bytes used to make up the next `nchars` characters */
-uint32_t utf8_nbytes(uint8_t *bytes, uint32_t nchars);
+uint32_t utf8_nbytes(uint8_t *bytes, uint32_t nbytes, uint32_t nchars);
/* true if `byte` is a unicode byte sequence start byte */
bool utf8_byte_is_unicode_start(uint8_t byte);
diff --git a/test/text.c b/test/text.c
index 43decda..459bd13 100644
--- a/test/text.c
+++ b/test/text.c
@@ -14,7 +14,7 @@ void test_add_text() {
uint32_t lines_added, cols_added;
struct text *t = text_create(10);
const char *txt = "This is line 1\n";
- text_append_at(t, 0, 0, (uint8_t *)txt, strlen(txt), &lines_added,
+ text_insert_at(t, 0, 0, (uint8_t *)txt, strlen(txt), &lines_added,
&cols_added);
ASSERT(text_num_lines(t) == 2,
"Expected text to have two lines after insertion");
@@ -25,7 +25,7 @@ void test_add_text() {
"Expected line 1 to be line 1");
const char *txt2 = "This is line 2\n";
- text_append_at(t, 1, 0, (uint8_t *)txt2, strlen(txt2), &lines_added,
+ text_insert_at(t, 1, 0, (uint8_t *)txt2, strlen(txt2), &lines_added,
&cols_added);
ASSERT(text_num_lines(t) == 3,
"Expected text to have three lines after second insertion");
@@ -34,7 +34,7 @@ void test_add_text() {
// simulate indentation
const char *txt3 = " ";
- text_append_at(t, 0, 0, (uint8_t *)txt3, strlen(txt3), &lines_added,
+ text_insert_at(t, 0, 0, (uint8_t *)txt3, strlen(txt3), &lines_added,
&cols_added);
ASSERT(text_num_lines(t) == 3,
"Expected text to have three lines after second insertion");
@@ -50,7 +50,7 @@ void test_delete_text() {
uint32_t lines_added, cols_added;
struct text *t = text_create(10);
const char *txt = "This is line 1";
- text_append_at(t, 0, 0, (uint8_t *)txt, strlen(txt), &lines_added,
+ text_insert_at(t, 0, 0, (uint8_t *)txt, strlen(txt), &lines_added,
&cols_added);
text_delete(t, 0, 12, 2);
@@ -65,7 +65,7 @@ void test_delete_text() {
"Expected line to be empty after many chars removed");
const char *txt2 = "This is line 1\nThis is line 2\nThis is line 3";
- text_append_at(t, 0, 0, (uint8_t *)txt2, strlen(txt2), &lines_added,
+ text_insert_at(t, 0, 0, (uint8_t *)txt2, strlen(txt2), &lines_added,
&cols_added);
ASSERT(text_num_lines(t) == 3,
"Expected to have three lines after inserting as many");
@@ -86,7 +86,7 @@ void test_delete_text() {
struct text *t3 = text_create(10);
const char *delete_me = "This is line๐ŸŽ™\nQ";
- text_append_at(t3, 0, 0, (uint8_t *)delete_me, strlen(delete_me),
+ text_insert_at(t3, 0, 0, (uint8_t *)delete_me, strlen(delete_me),
&lines_added, &cols_added);
text_delete(t3, 0, 13, 1);
struct text_chunk top_line = text_get_line(t3, 0);
@@ -97,10 +97,19 @@ void test_delete_text() {
ASSERT(text_num_lines(t3) == 1,
"Expected text to have one line after deleting newline");
+ struct text *t4 = text_create(10);
+ const char *deletable_text = "Only one line kinda";
+ text_append(t4, (uint8_t *)deletable_text, strlen(deletable_text),
+ &lines_added, &cols_added);
+ text_delete(t4, 0, 19, 1);
+ ASSERT(text_num_lines(t4) == 1, "Expected the line to still be there");
+ ASSERT(text_line_length(t4, 0) == 19,
+ "Expected nothing to have happened to the line");
+
// test utf-8
struct text *t2 = text_create(10);
const char *txt3 = "Emojis: ๐Ÿ‡ซ๐Ÿ‡ฎ ๐Ÿฎ\n";
- text_append_at(t2, 0, 0, (uint8_t *)txt3, strlen(txt3), &lines_added,
+ text_insert_at(t2, 0, 0, (uint8_t *)txt3, strlen(txt3), &lines_added,
&cols_added);
// TODO: Fix when graphemes are implemented, should be 11, right now it counts
diff --git a/test/utf8.c b/test/utf8.c
index 5b020c3..88e6a8c 100644
--- a/test/utf8.c
+++ b/test/utf8.c
@@ -3,10 +3,12 @@
#include "test.h"
#include "wchar.h"
+#include <string.h>
+
void test_nchars_nbytes() {
- ASSERT(utf8_nchars((uint8_t *)"๐Ÿ‘ด", 2) == 1,
+ ASSERT(utf8_nchars((uint8_t *)"๐Ÿ‘ด", strlen("๐Ÿ‘ด")) == 1,
"Expected old man emoji to be 1 char");
- ASSERT(utf8_nbytes((uint8_t *)"๐Ÿ‘ด", 1) == 4,
+ ASSERT(utf8_nbytes((uint8_t *)"๐Ÿ‘ด", strlen("๐Ÿ‘ด"), 1) == 4,
"Expected old man emoji to be 4 bytes");
}