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:
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);