ledit

Text editor (WIP)
git clone git://lumidify.org/ledit.git (fast, but not encrypted)
git clone https://lumidify.org/git/ledit.git (encrypted, but very slow)
Log | Files | Refs | README | LICENSE

commit bc282da2c2bfd820f810d1e03bc829fe3928f3e5
parent 9df066a3d594aeeca41677744f0f29a81901f124
Author: lumidify <nobody@lumidify.org>
Date:   Sun, 24 Oct 2021 13:48:53 +0200

Add basic (buggy) support for command repetition

Diffstat:
Mkeys_basic.c | 186++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Mkeys_basic_config.h | 2++
Mmemory.c | 8++++++++
Mmemory.h | 1+
4 files changed, 171 insertions(+), 26 deletions(-)

diff --git a/keys_basic.c b/keys_basic.c @@ -28,6 +28,22 @@ /* this is supposed to be global for all buffers */ txtbuf *paste_buffer = NULL; +struct repetition_stack_elem { + char *key_text; + int len; + KeySym sym; + unsigned int key_state; + int lang_index; +}; + +static struct { + int replaying; + size_t len, alloc, cur_pos; + struct repetition_stack_elem *stack; + size_t tmp_len, tmp_alloc; + struct repetition_stack_elem *tmp_stack; +} repetition_stack = {0, 0, 0, 0, NULL, 0, 0, NULL}; + struct key_stack_elem { enum key_type key; enum key_type followup; /* allowed keys to complete the keybinding */ @@ -46,6 +62,14 @@ static struct { struct key_stack_elem *stack; } key_stack = {0, 0, NULL}; +/* No, this isn't actually a stack. So what? */ +static struct repetition_stack_elem *push_repetition_stack(void); +static void finalize_repetition_stack(void); +static struct repetition_stack_elem *get_cur_repetition_stack_elem(void); +static void unwind_repetition_stack(void); +static void advance_repetition_stack(void); +static void discard_repetition_stack(void); + static struct key_stack_elem *push_key_stack(void); static struct key_stack_elem *peek_key_stack(void); static struct key_stack_elem *pop_key_stack(void); @@ -86,7 +110,7 @@ push_key_stack(void) { e->data1 = 0; e->data2 = 0; key_stack.len++; - return &key_stack.stack[key_stack.len - 1]; + return e; } /* Note: for peek and pop, the returned element is only valid @@ -113,6 +137,67 @@ clear_key_stack(void) { key_stack.len = 0; } +static struct repetition_stack_elem * +push_repetition_stack(void) { + struct repetition_stack_elem *e; + if (repetition_stack.tmp_len >= repetition_stack.tmp_alloc) { + size_t new_alloc = repetition_stack.tmp_alloc > 0 ? repetition_stack.tmp_alloc * 2 : 4; + repetition_stack.tmp_stack = ledit_realloc( + repetition_stack.tmp_stack, + new_alloc * sizeof(struct repetition_stack_elem) + ); + repetition_stack.tmp_alloc = new_alloc; + } + e = &repetition_stack.tmp_stack[repetition_stack.tmp_len]; + e->key_text = NULL; + e->len = 0; + e->sym = 0; + e->key_state = 0; + e->lang_index = 0; + repetition_stack.tmp_len++; + return e; +} + +static struct repetition_stack_elem * +get_cur_repetition_stack_elem(void) { + if (repetition_stack.cur_pos >= repetition_stack.len) + return NULL; + return &repetition_stack.stack[repetition_stack.cur_pos]; +} + +static void +unwind_repetition_stack(void) { + repetition_stack.cur_pos = 0; +} + +static void +discard_repetition_stack(void) { + if (repetition_stack.replaying) + return; + repetition_stack.tmp_len = 0; +} + +static void +advance_repetition_stack(void) { + repetition_stack.cur_pos++; +} + +static void +finalize_repetition_stack(void) { + if (repetition_stack.replaying) + return; + size_t tmp; + struct repetition_stack_elem *tmpstack; + repetition_stack.len = repetition_stack.tmp_len; + repetition_stack.tmp_len = 0; + tmp = repetition_stack.alloc; + repetition_stack.alloc = repetition_stack.tmp_alloc; + repetition_stack.tmp_alloc = tmp; + tmpstack = repetition_stack.stack; + repetition_stack.stack = repetition_stack.tmp_stack; + repetition_stack.tmp_stack = tmpstack; +} + /* get the new line and softline when moving 'movement' softlines up or down (negative means up, positive means down) */ static void @@ -295,6 +380,7 @@ key_d_cb(ledit_buffer *buffer, int line, int char_pos, enum key_type type) { line, char_pos ); ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index); + finalize_repetition_stack(); } static struct action @@ -523,6 +609,7 @@ move_cursor_left_right(ledit_buffer *buffer, int dir) { } else if (buffer->common->mode == NORMAL) { ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index); } + discard_repetition_stack(); } clear_key_stack(); } @@ -621,6 +708,8 @@ escape_key(ledit_buffer *buffer, char *text, int len) { (void)text; (void)len; clear_key_stack(); /* just in case... */ + if (buffer->common->mode == INSERT) + finalize_repetition_stack(); if (buffer->common->mode == INSERT && (buffer->sel.line1 != buffer->sel.line2 || buffer->sel.byte1 != buffer->sel.byte2)) { @@ -708,6 +797,7 @@ move_cursor_up_down(ledit_buffer *buffer, int dir) { } else if (buffer->common->mode == NORMAL) { ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index); } + discard_repetition_stack(); } clear_key_stack(); } @@ -749,6 +839,7 @@ cursor_to_beginning(ledit_buffer *buffer, char *text, int len) { buffer, buffer->cur_line, buffer->cur_index ); } + discard_repetition_stack(); } clear_key_stack(); return (struct action){ACTION_NONE, NULL}; @@ -788,6 +879,7 @@ enter_commandedit(ledit_buffer *buffer, char *text, int len) { ledit_window_set_bottom_bar_cursor(buffer->window, 1); ledit_command_set_type(CMD_EDIT); ledit_window_set_bottom_bar_text_shown(buffer->window, 1); + discard_repetition_stack(); return (struct action){ACTION_GRABKEY, &ledit_command_key_handler}; } @@ -799,6 +891,7 @@ enter_searchedit_forward(ledit_buffer *buffer, char *text, int len) { ledit_window_set_bottom_bar_cursor(buffer->window, 1); ledit_command_set_type(CMD_EDITSEARCH); ledit_window_set_bottom_bar_text_shown(buffer->window, 1); + discard_repetition_stack(); return (struct action){ACTION_GRABKEY, &ledit_command_key_handler}; } @@ -810,6 +903,7 @@ enter_searchedit_backward(ledit_buffer *buffer, char *text, int len) { ledit_window_set_bottom_bar_cursor(buffer->window, 1); ledit_command_set_type(CMD_EDITSEARCHB); ledit_window_set_bottom_bar_text_shown(buffer->window, 1); + discard_repetition_stack(); return (struct action){ACTION_GRABKEY, &ledit_command_key_handler}; } @@ -819,6 +913,7 @@ key_search_next(ledit_buffer *buffer, char *text, int len) { (void)text; (void)len; search_next(buffer); + discard_repetition_stack(); return (struct action){ACTION_NONE, NULL}; } @@ -827,6 +922,7 @@ key_search_prev(ledit_buffer *buffer, char *text, int len) { (void)text; (void)len; search_prev(buffer); + discard_repetition_stack(); return (struct action){ACTION_NONE, NULL}; } @@ -838,6 +934,7 @@ show_line(ledit_buffer *buffer, char *text, int len) { char *str = ledit_malloc(textlen + 1); snprintf(str, textlen + 1, "Line %d of %d", buffer->cur_line + 1, buffer->lines_num); ledit_window_show_message(buffer->window, str, textlen); + discard_repetition_stack(); return (struct action){ACTION_NONE, NULL}; } @@ -850,6 +947,7 @@ undo(ledit_buffer *buffer, char *text, int len) { ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line); ledit_buffer_undo(buffer); ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index); + finalize_repetition_stack(); return (struct action){ACTION_NONE, NULL}; } @@ -861,6 +959,7 @@ redo(ledit_buffer *buffer, char *text, int len) { ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line); ledit_buffer_redo(buffer); ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index); + finalize_repetition_stack(); return (struct action){ACTION_NONE, NULL}; } @@ -877,6 +976,7 @@ clipcopy(ledit_buffer *buffer, char *text, int len) { (void)len; /* FIXME: abstract this through buffer */ clipboard_primary_to_clipboard(buffer->window); + discard_repetition_stack(); return (struct action){ACTION_NONE, NULL}; } @@ -885,53 +985,87 @@ clippaste(ledit_buffer *buffer, char *text, int len) { (void)text; (void)len; ledit_buffer_paste_clipboard(buffer); + finalize_repetition_stack(); return (struct action){ACTION_NONE, NULL}; } -struct action -basic_key_handler(ledit_buffer *buffer, XEvent *event, int lang_index) { - char buf[64]; - KeySym sym; - int n; - +static struct action +handle_key(ledit_buffer *buffer, char *key_text, int len, KeySym sym, unsigned int key_state, int lang_index, int *found) { + struct action act = {ACTION_NONE, NULL}; struct key *cur_keys = keys[lang_index].keys; int num_keys = keys[lang_index].num_keys; - unsigned int key_state = event->xkey.state; - preprocess_key(buffer->window, event, &sym, buf, sizeof(buf), &n); - - int found = 0; struct key_stack_elem *e = peek_key_stack(); - /* FIXME: only hide when actually necessary */ - ledit_window_hide_message(buffer->window); - struct action act; + *found = 0; for (int i = 0; i < num_keys; i++) { if (cur_keys[i].text) { - if (n > 0 && + if (len > 0 && (cur_keys[i].modes & buffer->common->mode) && (!e || (e->key & cur_keys[i].prev_keys)) && - ((!strncmp(cur_keys[i].text, buf, n) && + ((!strncmp(cur_keys[i].text, key_text, len) && match_key(cur_keys[i].mods, key_state & ~ShiftMask)) || cur_keys[i].text[0] == '\0')) { /* FIXME: seems a bit hacky to remove shift, but it is needed to make keys that use shift match */ - act = cur_keys[i].func(buffer, buf, n); - found = 1; + act = cur_keys[i].func(buffer, key_text, len); + *found = 1; break; } } else if ((cur_keys[i].modes & buffer->common->mode) && cur_keys[i].keysym == sym && match_key(cur_keys[i].mods, key_state)) { - act = cur_keys[i].func(buffer, buf, n); - found = 1; + act = cur_keys[i].func(buffer, key_text, len); + *found = 1; break; } } + return act; +} + +static struct action +repeat_command(ledit_buffer *buffer, char *text, int len) { + (void)buffer; + (void)text; + (void)len; + int found; + repetition_stack.replaying = 1; + clear_key_stack(); + unwind_repetition_stack(); + struct repetition_stack_elem *e = get_cur_repetition_stack_elem(); + while (e) { + (void)handle_key(buffer, e->key_text, e->len, e->sym, e->key_state, e->lang_index, &found); + advance_repetition_stack(); + e = get_cur_repetition_stack_elem(); + } + repetition_stack.replaying = 0; + discard_repetition_stack(); + clear_key_stack(); + return (struct action){ACTION_NONE, NULL}; +} + +struct action +basic_key_handler(ledit_buffer *buffer, XEvent *event, int lang_index) { + char buf[64]; + KeySym sym; + int n; + + unsigned int key_state = event->xkey.state; + preprocess_key(buffer->window, event, &sym, buf, sizeof(buf), &n); + + struct repetition_stack_elem *re = push_repetition_stack(); + re->key_text = ledit_strndup(buf, (size_t)n); + re->len = n; + re->sym = sym; + re->key_state = key_state; + re->lang_index = lang_index; + + /* FIXME: only hide when actually necessary */ + ledit_window_hide_message(buffer->window); + int found = 0; + struct action act = handle_key(buffer, buf, n, sym, key_state, lang_index, &found); + /* FIXME: only do this when necessary */ - if (found) { + if (found) ledit_buffer_ensure_cursor_shown(buffer); - return act; - } else { - /* FIXME: maybe show error */ - return (struct action){ACTION_NONE, NULL}; - } + /* FIXME: maybe show error if not found */ + return act; } diff --git a/keys_basic_config.h b/keys_basic_config.h @@ -56,6 +56,7 @@ static struct action key_search_prev(ledit_buffer *buffer, char *text, int len); static struct action undo(ledit_buffer *buffer, char *text, int len); static struct action redo(ledit_buffer *buffer, char *text, int len); static struct action insert_mode_insert_text(ledit_buffer *buffer, char *text, int len); +static struct action repeat_command(ledit_buffer *buffer, char *text, int len); /* FIXME: maybe sort these and use binary search -> but that would mess with the catch-all keys */ @@ -98,6 +99,7 @@ static struct key keys_en[] = { {"N", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &key_search_prev}, {"u", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &undo}, {"U", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &redo}, + {".", 0, 0, NORMAL, KEY_ANY, KEY_ANY, &repeat_command}, /* FIXME: only allow after finished key sequence */ {"z", ControlMask, 0, INSERT, KEY_ANY, KEY_ANY, &undo}, {"y", ControlMask, 0, INSERT, KEY_ANY, KEY_ANY, &redo}, {"", 0, 0, INSERT, KEY_ANY, KEY_ANY, &insert_mode_insert_text} diff --git a/memory.c b/memory.c @@ -16,6 +16,14 @@ ledit_strdup(const char *s) { return str; } +char * +ledit_strndup(const char *s, size_t n) { + char *str = strndup(s, n); + if (!str) + fatal_err("Out of memory.\n"); + return str; +} + void * ledit_malloc(size_t size) { void *ptr = malloc(size); diff --git a/memory.h b/memory.h @@ -1,4 +1,5 @@ char *ledit_strdup(const char *s); +char *ledit_strndup(const char *s, size_t n); void *ledit_malloc(size_t size); void *ledit_calloc(size_t nmemb, size_t size); void *ledit_realloc(void *ptr, size_t size);