commit 250e61a764d867385a09548558b0df6c28af8036
parent 3d0707ecc637d39e0f853d036c592bbcbe7675f7
Author: lumidify <nobody@lumidify.org>
Date: Sat, 6 Nov 2021 23:42:41 +0100
Add commands for jumping to character
Diffstat:
5 files changed, 179 insertions(+), 9 deletions(-)
diff --git a/buffer.c b/buffer.c
@@ -917,6 +917,22 @@ ledit_buffer_next_cursor_pos(ledit_buffer *buffer, int line, int byte) {
return ll->len;
}
+int
+ledit_buffer_prev_cursor_pos(ledit_buffer *buffer, int line, int byte) {
+ int nattrs;
+ ledit_line *ll = ledit_buffer_get_line(buffer, line);
+ int c = line_byte_to_char(ll, byte);
+ int cur_byte = ledit_line_prev_utf8(ll, byte);
+ const PangoLogAttr *attrs =
+ pango_layout_get_log_attrs_readonly(ll->layout, &nattrs);
+ for (int i = c - 1; i >= 0; i--) {
+ if (attrs[i].is_cursor_position)
+ return cur_byte;
+ cur_byte = ledit_line_prev_utf8(ll, cur_byte);
+ }
+ return 0;
+}
+
static int
line_next_word(ledit_line *line, int byte, int char_index, int wrapped_line, int *char_ret, int *real_byte_ret) {
int c, nattrs;
diff --git a/buffer.h b/buffer.h
@@ -58,6 +58,7 @@ void ledit_x_softline_to_pos(ledit_line *line, int x, int softline, int *pos_ret
int ledit_line_next_utf8(ledit_line *line, int index);
int ledit_line_prev_utf8(ledit_line *line, int index);
int ledit_buffer_next_cursor_pos(ledit_buffer *buffer, int line, int byte);
+int ledit_buffer_prev_cursor_pos(ledit_buffer *buffer, int line, int byte);
void ledit_buffer_next_word(ledit_buffer *buffer, int line, int byte, int num_repeat, int *line_ret, int *byte_ret, int *real_byte_ret);
void ledit_buffer_next_word_end(ledit_buffer *buffer, int line, int byte, int num_repeat, int *line_ret, int *byte_ret, int *real_byte_ret);
diff --git a/keys_basic.c b/keys_basic.c
@@ -56,6 +56,8 @@ static struct {
struct repetition_stack_elem *tmp_stack;
} repetition_stack = {0, 0, 0, 0, NULL, 0, 0, NULL};
+typedef void (*motion_callback)(ledit_buffer *buffer, int line, int char_pos, enum key_type type);
+
struct key_stack_elem {
enum key_type key;
enum key_type followup; /* allowed keys to complete the keybinding */
@@ -63,7 +65,7 @@ struct key_stack_elem {
* line and char_pos already include the repetition stored in this stack
* element; type is the type of motion command (used to determine if
* the command should operate on lines or chars) */
- void (*motion_cb)(ledit_buffer *buffer, int line, int char_pos, enum key_type type);
+ motion_callback motion_cb;
int count; /* number of repetitions */
int data1; /* misc. data 1 */
int data2; /* misc. data 2 */
@@ -74,6 +76,8 @@ static struct {
struct key_stack_elem *stack;
} key_stack = {0, 0, NULL};
+static struct action (*grab_char_cb)(ledit_buffer *buffer, char *text, int len) = NULL;
+
void
basic_key_cleanup(void) {
/* this should be safe since push_repetition_stack sets all new
@@ -1590,12 +1594,157 @@ clippaste(ledit_buffer *buffer, char *text, int len) {
return (struct action){ACTION_NONE, NULL};
}
+static int
+get_key_repeat_and_motion_cb(motion_callback *cb_ret) {
+ int num = 1;
+ struct key_stack_elem *e = pop_key_stack();
+ if (e != NULL) {
+ if (e->key & KEY_NUMBER) {
+ num = e->count > 0 ? e->count : 1;
+ e = pop_key_stack();
+ }
+ if (e != NULL)
+ num *= (e->count > 0 ? e->count : 1);
+ }
+ if (cb_ret != NULL && e != NULL && e->motion_cb != NULL)
+ *cb_ret = e->motion_cb;
+ return num;
+}
+
+/* FIXME: make sure the found position is valid cursor position? */
+static int
+search_str_backwards(char *haystack, int hlen, char *needle, int nlen, int start_index) {
+ if (start_index < 0 || start_index > hlen)
+ return -1;
+ int new_index = start_index;
+ for (new_index--; new_index >= 0; new_index--) {
+ if (!strncmp(haystack + new_index, needle, nlen))
+ return new_index;
+ }
+ return -1;
+}
+
+static int
+search_str_forwards(char *haystack, int hlen, char *needle, int nlen, int start_index) {
+ if (start_index < 0 || start_index >= hlen)
+ return -1;
+ /* duplicate so it is nul-terminated */
+ char *search_str = ledit_strndup(needle, nlen);
+ char *res = strstr(haystack + start_index + 1, search_str);
+ free(search_str);
+ /* FIXME: is this legal? */
+ if (res)
+ return (int)(res - haystack);
+ else
+ return -1;
+}
+
+/* just to make the macro below works for all cases */
+/* FIXME: is there a more elegant way to do this? */
+static int
+dummy_cursor_helper(ledit_buffer *buffer, int line, int index) {
+ (void)buffer;
+ (void)line;
+ return index;
+}
+
+/* FIXME: call normalize on line first? */
+/* FIXME: add checks to functions that current mode is supported */
+
+/* name is the name of the generated pair of functions
+ search_func is used to get the next index (possibly called
+ repeatedly if there is a repeat number on the key stack)
+ funcm = func motion, funcn = func normal, funcv = func visual
+ -> these are called to modify the index returned by search_func
+ cur_funcm is called to get the index for a motion callback
+ cur_funcn is called to position the cursor in normal mode
+ cur_funcv is called to position the cursor in visual mode */
+#define GEN_MOVE_TO_CHAR(name, search_func, cur_funcm, cur_funcn, cur_funcv) \
+static struct action \
+name##_cb(ledit_buffer *buffer, char *text, int len) { \
+ motion_callback cb = NULL; \
+ int num = get_key_repeat_and_motion_cb(&cb); \
+ ledit_line *ll = ledit_buffer_get_line(buffer, buffer->cur_line); \
+ int new_index = buffer->cur_index; \
+ for (int i = 0; i < num; i++) { \
+ new_index = search_func(ll->text, ll->len, text, len, new_index); \
+ if (new_index == -1) \
+ break; \
+ } \
+ if (new_index >= 0) { \
+ if (cb != NULL) { \
+ new_index = cur_funcm( \
+ buffer, buffer->cur_line, new_index \
+ ); \
+ cb(buffer, buffer->cur_line, new_index, KEY_MOTION_CHAR); \
+ } else { \
+ if (buffer->common->mode == VISUAL) { \
+ buffer->cur_index = cur_funcv( \
+ buffer, buffer->cur_line, new_index \
+ ); \
+ ledit_buffer_set_selection( \
+ buffer, \
+ buffer->sel.line1, buffer->sel.byte1, \
+ buffer->cur_line, buffer->cur_index \
+ ); \
+ } else { \
+ buffer->cur_index = cur_funcn( \
+ buffer, buffer->cur_line, new_index \
+ ); \
+ ledit_buffer_set_line_cursor_attrs( \
+ buffer, buffer->cur_line, buffer->cur_index \
+ ); \
+ } \
+ discard_repetition_stack(); \
+ } \
+ } \
+ clear_key_stack(); \
+ grab_char_cb = NULL; \
+ return (struct action){ACTION_NONE, NULL}; \
+} \
+ \
+static struct action \
+name(ledit_buffer *buffer, char *text, int len) { \
+ (void)buffer; \
+ (void)text; \
+ (void)len; \
+ grab_char_cb = &name##_cb; \
+ return (struct action){ACTION_NONE, NULL}; \
+}
+
+/* FIXME: more sensible names */
+/* FIXME: dummy_cursor_helper is kind of ugly */
+GEN_MOVE_TO_CHAR(
+ find_next_char_forwards, search_str_forwards,
+ dummy_cursor_helper, ledit_buffer_prev_cursor_pos, dummy_cursor_helper
+)
+GEN_MOVE_TO_CHAR(
+ find_next_char_backwards, search_str_backwards,
+ ledit_buffer_next_cursor_pos, ledit_buffer_next_cursor_pos, ledit_buffer_next_cursor_pos
+)
+GEN_MOVE_TO_CHAR(
+ find_char_forwards, search_str_forwards,
+ ledit_buffer_next_cursor_pos, dummy_cursor_helper, dummy_cursor_helper
+)
+GEN_MOVE_TO_CHAR(
+ find_char_backwards, search_str_backwards,
+ dummy_cursor_helper, dummy_cursor_helper, dummy_cursor_helper
+)
+
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;
struct key_stack_elem *e = peek_key_stack();
+ /* FIXME: allow to escape this grabbing somehow */
+ /* -> I guess escape *does* actually work because it
+ results in ascii 1b (escape) in this string, which
+ will usually not be found in the text (but this is
+ a bit of a hack) */
+ if (len > 0 && grab_char_cb) {
+ *found = 1;
+ return grab_char_cb(buffer, key_text, len);
+ }
*found = 0;
for (int i = 0; i < num_keys; i++) {
if (cur_keys[i].text) {
@@ -1607,19 +1756,17 @@ handle_key(ledit_buffer *buffer, char *key_text, int len, KeySym sym, unsigned i
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, key_text, len);
*found = 1;
- break;
+ return cur_keys[i].func(buffer, key_text, len);
}
} 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, key_text, len);
*found = 1;
- break;
+ return cur_keys[i].func(buffer, key_text, len);
}
}
- return act;
+ return (struct action){ACTION_NONE, NULL};
}
static struct action
diff --git a/keys_basic_config.h b/keys_basic_config.h
@@ -80,6 +80,10 @@ static struct action append_after_eol(ledit_buffer *buffer, char *text, int len)
static struct action append_after_cursor(ledit_buffer *buffer, char *text, int len);
static struct action append_line_above(ledit_buffer *buffer, char *text, int len);
static struct action append_line_below(ledit_buffer *buffer, char *text, int len);
+static struct action find_next_char_forwards(ledit_buffer *buffer, char *text, int len);
+static struct action find_next_char_backwards(ledit_buffer *buffer, char *text, int len);
+static struct action find_char_forwards(ledit_buffer *buffer, char *text, int len);
+static struct action find_char_backwards(ledit_buffer *buffer, char *text, int len);
/* FIXME: maybe sort these and use binary search
-> but that would mess with the catch-all keys */
@@ -153,6 +157,10 @@ static struct key keys_en[] = {
{"o", 0, 0, NORMAL, KEY_ANY, KEY_ANY, &append_line_below},
{"m", 0, 0, NORMAL, KEY_ANY, KEY_ANY, &mark_line},
{"'", 0, 0, NORMAL, KEY_ANY, KEY_ANY, &jump_to_mark},
+ {"t", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBERALLOWED, &find_next_char_forwards},
+ {"T", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBERALLOWED, &find_next_char_backwards},
+ {"f", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBERALLOWED, &find_char_forwards},
+ {"F", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBERALLOWED, &find_char_backwards},
{"", 0, 0, INSERT, KEY_ANY, KEY_ANY, &insert_mode_insert_text}
};
diff --git a/keys_command_config.h b/keys_command_config.h
@@ -29,8 +29,6 @@ static struct key keys_en[] = {
{"", 0, 0, CMD_EDIT, &edit_insert_text},
{"", 0, 0, CMD_EDITSEARCH, &edit_insert_text},
{"", 0, 0, CMD_EDITSEARCHB, &edit_insert_text},
- /* FIXME: also allow non-text keys for marks?
- OpenBSD nvi seems to allow it, at least sort of. */
{"", 0, 0, CMD_MARKLINE, &mark_line},
{"", 0, 0, CMD_JUMPTOMARK, &jump_to_mark}
};