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 52cea73bf0e43483210cb94e5177989bc524a30c
parent 174e6af4e28e5dec7b1402de9b15174357898865
Author: lumidify <nobody@lumidify.org>
Date:   Wed, 12 May 2021 21:47:17 +0200

Add initial support for deletion

Diffstat:
Mbuffer.c | 275++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Mbuffer.h | 10++++++++++
Mledit.c | 342+++++++++++++++++++++++++++++++++++++++++--------------------------------------
3 files changed, 452 insertions(+), 175 deletions(-)

diff --git a/buffer.c b/buffer.c @@ -138,8 +138,9 @@ init_line(ledit_buffer *buffer, ledit_line *line) { line->parent_buffer = buffer; line->layout = pango_layout_new(buffer->state->context); pango_layout_set_width(line->layout, (buffer->state->w - 10) * PANGO_SCALE); - pango_layout_set_font_description(line->layout, buffer->state->font); - pango_layout_set_wrap(line->layout, PANGO_WRAP_WORD_CHAR); + pango_layout_set_font_description(line->layout, buffer->state->font); + pango_layout_set_wrap(line->layout, PANGO_WRAP_WORD_CHAR); + pango_layout_set_attributes(line->layout, basic_attrs); line->text = NULL; line->cap = line->len = 0; line->cache_index = -1; @@ -185,18 +186,27 @@ ledit_append_line(ledit_buffer *buffer, int line_index, int text_index) { } void -ledit_delete_line_entry(ledit_buffer *buffer, int index) { - g_object_unref(buffer->lines[index].layout); - free(buffer->lines[index].text); - if (index < buffer->lines_num - 1) +ledit_delete_line_entries(ledit_buffer *buffer, int index1, int index2) { + for (int i = index1; i <= index2; i++) { + g_object_unref(buffer->lines[i].layout); + free(buffer->lines[i].text); + } + if (index2 < buffer->lines_num - 1) { memmove( - buffer->lines + index, buffer->lines + index + 1, - (buffer->lines_num - index - 1) * sizeof(ledit_line) + buffer->lines + index1, buffer->lines + index2 + 1, + (buffer->lines_num - index2 - 1) * sizeof(ledit_line) ); - buffer->lines_num--; + } + buffer->lines_num -= index2 - index1 + 1; + /* FIXME: avoid this by just subtracting the heights */ recalc_line_size_absolute(buffer); } +void +ledit_delete_line_entry(ledit_buffer *buffer, int index) { + ledit_delete_line_entries(buffer, index, index); +} + /* FIXME: use some sort of gap buffer (that would make this function slightly more useful...) */ ledit_line * @@ -270,3 +280,250 @@ ledit_delete_unicode_char(ledit_buffer *buffer, int line_index, int byte_index, recalc_single_line_size(buffer, line_index); return new_index; } + +static void +delete_line_section(ledit_buffer *buffer, int line, int start, int length) { + ledit_line *l = &buffer->lines[line]; + memmove(l->text + start, l->text + start + length, l->len - start - length); + l->len -= length; + pango_layout_set_text(l->layout, l->text, l->len); + recalc_single_line_size(buffer, line); + l->dirty = 1; +} + +void +ledit_pos_to_x_softline(ledit_line *line, int pos, int *x_ret, int *softline_ret) { + pango_layout_index_to_line_x(line->layout, pos, 0, softline_ret, x_ret); + /* FIXME: do these lines need to be unref'd? */ + PangoLayoutLine *pango_line = pango_layout_get_line_readonly(line->layout, *softline_ret); + /* add left margin to x position if line is aligned right */ + if (pango_line->resolved_dir == PANGO_DIRECTION_RTL) { + PangoRectangle rect; + pango_layout_line_get_extents(pango_line, NULL, &rect); + *x_ret += (line->w * PANGO_SCALE - rect.width); + } + /* if in normal mode, change position to the middle of the + current rectangle so that moving around won't jump weirdly */ + /* FIXME: also in visual? */ + if (line->parent_buffer->state->mode == NORMAL) { + PangoRectangle rect; + pango_layout_index_to_pos(line->layout, pos, &rect); + *x_ret += rect.width / 2; + } +} + +/* FIXME: change this to return int */ +void +ledit_x_softline_to_pos(ledit_line *line, int x, int softline, int *pos_ret) { + int trailing = 0; + int x_relative = x; + PangoLayoutLine *pango_line = + pango_layout_get_line_readonly(line->layout, softline); + /* x is absolute, so the margin at the left needs to be subtracted */ + if (pango_line->resolved_dir == PANGO_DIRECTION_RTL) { + PangoRectangle rect; + pango_layout_line_get_extents(pango_line, NULL, &rect); + x_relative -= (line->w * PANGO_SCALE - rect.width); + } + pango_layout_line_x_to_index( + pango_line, x_relative, pos_ret, &trailing + ); + /* if in insert mode, snap to the nearest border between graphemes */ + if (line->parent_buffer->state->mode == INSERT) { + while (trailing > 0) { + trailing--; + (*pos_ret)++; + /* utf8 stuff */ + while (*pos_ret < line->len && + ((line->text[*pos_ret] & 0xC0) == 0x80)) + (*pos_ret)++; + } + } +} + +/* FIXME: cursor jumps weirdly */ +/* FIXME: use at least somewhat sensible variable names */ +void +ledit_delete_range( + ledit_buffer *buffer, int line_based, + int line_index1, int byte_index1, + int line_index2, int byte_index2, + int *new_line_ret, int *new_byte_ret) { + if (line_based) { + int x, softline1, softline2; + ledit_line *line1 = ledit_get_line(buffer, line_index1); + ledit_pos_to_x_softline(line1, byte_index1, &x, &softline1); + if (line_index1 == line_index2) { + int x_useless; + pango_layout_index_to_line_x(line1->layout, byte_index2, 0, &softline2, &x_useless); + int l1 = softline1 < softline2 ? softline1 : softline2; + int l2 = softline1 < softline2 ? softline2 : softline1; + int softlines = pango_layout_get_line_count(line1->layout); + PangoLayoutLine *pl1 = pango_layout_get_line_readonly(line1->layout, l1); + PangoLayoutLine *pl2 = pango_layout_get_line_readonly(line1->layout, l2); + /* don't delete entire line of it is the last one remaining */ + if (l1 == 0 && l2 == softlines - 1 && buffer->lines_num > 1) { + ledit_delete_line_entry(buffer, line_index1); + /* note: line_index1 is now the index of the next + line since the current one was just deleted */ + if (line_index1 < buffer->lines_num) { + *new_line_ret = line_index1; + ledit_x_softline_to_pos( + ledit_get_line(buffer, line_index1), + x, 0, new_byte_ret + ); + } else { + /* note: logically, this must be >= 0 because + buffer->lines_num > 1 && line_index1 >= buffer->lines_num */ + *new_line_ret = line_index1 - 1; + ledit_line *prevline = ledit_get_line(buffer, line_index1 - 1); + softlines = pango_layout_get_line_count(prevline->layout); + ledit_x_softline_to_pos(prevline, x, softlines - 1, new_byte_ret); + } + } else { + /* FIXME: sanity checks that the length is actually positive, etc. */ + delete_line_section( + buffer, line_index1, pl1->start_index, + pl2->start_index + pl2->length - pl1->start_index + ); + if (l2 == softlines - 1 && line_index1 < buffer->lines_num - 1) { + *new_line_ret = line_index1 + 1; + ledit_x_softline_to_pos( + ledit_get_line(buffer, line_index1 + 1), + x, 0, new_byte_ret + ); + } else if (l2 < softlines - 1) { + *new_line_ret = line_index1; + ledit_x_softline_to_pos( + ledit_get_line(buffer, line_index1), + x, l1, new_byte_ret + ); + } else if (l1 > 0) { + *new_line_ret = line_index1; + ledit_x_softline_to_pos( + ledit_get_line(buffer, line_index1), + x, l1 - 1, new_byte_ret + ); + } else { + /* the line has been emptied and is the last line remaining */ + *new_line_ret = 0; + *new_byte_ret = 0; + } + } + } else { + int x_useless, sl1, sl2; + int l1, l2, b1, b2; + if (line_index1 < line_index2) { + l1 = line_index1; + b1 = byte_index1; + l2 = line_index2; + b2 = byte_index2; + } else { + l1 = line_index2; + b1 = byte_index2; + l2 = line_index1; + b2 = byte_index1; + } + ledit_line *ll1 = ledit_get_line(buffer, l1); + ledit_line *ll2 = ledit_get_line(buffer, l2); + pango_layout_index_to_line_x(ll1->layout, b1, 0, &sl1, &x_useless); + pango_layout_index_to_line_x(ll2->layout, b2, 0, &sl2, &x_useless); + PangoLayoutLine *pl1 = pango_layout_get_line_readonly(ll1->layout, sl1); + PangoLayoutLine *pl2 = pango_layout_get_line_readonly(ll2->layout, sl2); + int softlines = pango_layout_get_line_count(ll2->layout); + if (sl1 == 0 && sl2 == softlines - 1) { + if (l1 == 0 && l2 == buffer->lines_num - 1) { + delete_line_section(buffer, l1, 0, ll1->len); + ledit_delete_line_entries(buffer, l1 + 1, l2); + *new_line_ret = 0; + *new_byte_ret = 0; + } else { + ledit_delete_line_entries(buffer, l1, l2); + if (l1 >= buffer->lines_num) { + *new_line_ret = buffer->lines_num - 1; + ledit_line *new_lline = ledit_get_line(buffer, *new_line_ret); + int new_softlines = pango_layout_get_line_count(new_lline->layout); + ledit_x_softline_to_pos(new_lline, x, new_softlines - 1, new_byte_ret); + } else { + *new_line_ret = l1; + ledit_x_softline_to_pos( + ledit_get_line(buffer, l1), + x, 0, new_byte_ret + ); + } + } + } else if (sl1 == 0) { + delete_line_section(buffer, l2, 0, pl2->start_index + pl2->length); + ledit_delete_line_entries(buffer, l1, l2 - 1); + *new_line_ret = l1; + ledit_x_softline_to_pos(ledit_get_line(buffer, l1), x, 0, new_byte_ret); + } else if (sl2 == softlines - 1) { + delete_line_section(buffer, l1, pl1->start_index, ll1->len - pl1->start_index); + ledit_delete_line_entries(buffer, l1 + 1, l2); + if (l1 + 1 >= buffer->lines_num) { + *new_line_ret = buffer->lines_num - 1; + ledit_line *new_lline = ledit_get_line(buffer, *new_line_ret); + int new_softlines = pango_layout_get_line_count(new_lline->layout); + ledit_x_softline_to_pos(new_lline, x, new_softlines - 1, new_byte_ret); + } else { + *new_line_ret = l1 + 1; + ledit_x_softline_to_pos( + ledit_get_line(buffer, l1 + 1), + x, 0, new_byte_ret + ); + } + } else { + /* FIXME: should this join the two lines? */ + delete_line_section(buffer, l1, pl1->start_index, ll1->len - pl1->start_index); + delete_line_section(buffer, l2, 0, pl2->start_index + pl2->length); + if (l2 > l1 + 1) + ledit_delete_line_entries(buffer, l1 + 1, l2 - 1); + *new_line_ret = l1 + 1; + ledit_x_softline_to_pos(ledit_get_line(buffer, l1 + 1), x, 0, new_byte_ret); + } + } + } else { + if (line_index1 == line_index2) { + int b1, b2; + if (byte_index1 < byte_index2) { + b1 = byte_index1; + b2 = byte_index2; + } else { + b1 = byte_index2; + b2 = byte_index1; + } + delete_line_section(buffer, line_index1, b1, b2 - b1); + *new_line_ret = line_index1; + *new_byte_ret = b1; + /* FIXME: this needs to be checked by calling code to + move cursor one back if in normal mode and at end + of line */ + } else { + int l1, l2, b1, b2; + if (line_index1 < line_index2) { + l1 = line_index1; + b1 = byte_index1; + l2 = line_index2; + b2 = byte_index2; + } else { + l1 = line_index2; + b1 = byte_index2; + l2 = line_index1; + b2 = byte_index1; + } + ledit_line *line1 = ledit_get_line(buffer, l1); + ledit_line *line2 = ledit_get_line(buffer, l2); + line1->len = b1; + if (b2 > 0) { + ledit_insert_text( + buffer, l1, b1, + line2->text + b2, + line2->len - b2 + ); + } + *new_line_ret = l1; + *new_byte_ret = b1; + ledit_delete_line_entries(buffer, l1 + 1, l2); + } + } +} diff --git a/buffer.h b/buffer.h @@ -1,5 +1,6 @@ typedef struct ledit_buffer ledit_buffer; +/* FIXME: size_t for len, etc. */ typedef struct { PangoLayout *layout; char *text; @@ -36,7 +37,16 @@ void ledit_wipe_line_cursor_attrs(ledit_buffer *buffer, int line); void ledit_insert_text(ledit_buffer *buffer, int line_index, int index, char *text, int len); void ledit_render_line(ledit_buffer *buffer, int line_index); void ledit_append_line(ledit_buffer *buffer, int line_index, int text_index); +void ledit_delete_line_entries(ledit_buffer *buffer, int index1, int index2); void ledit_delete_line_entry(ledit_buffer *buffer, int index); ledit_line *ledit_get_line(ledit_buffer *buffer, int index); int ledit_line_visible(ledit_buffer *buffer, int index); int ledit_delete_unicode_char(ledit_buffer *buffer, int line_index, int byte_index, int dir); +void ledit_delete_range( + ledit_buffer *buffer, int line_based, + int line_index1, int byte_index1, + int line_index2, int byte_index2, + int *new_line_ret, int *new_byte_ret +); +void ledit_pos_to_x_softline(ledit_line *line, int pos, int *x_ret, int *softline_ret); +void ledit_x_softline_to_pos(ledit_line *line, int x, int softline, int *pos_ret); diff --git a/ledit.c b/ledit.c @@ -31,24 +31,32 @@ enum key_type { KEY_NONE = 0, KEY_MISC = 1, KEY_CHAR = 2, - KEY_MOTION = 4, - KEY_NUMBER = 8, - KEY_NUMBERALLOWED = 16, + KEY_MOTION_CHAR = 4, + KEY_MOTION_LINE = 8, + KEY_MOTION = 4|8, + KEY_NUMBER = 16, + KEY_NUMBERALLOWED = 32, KEY_ANY = 0xFF }; struct key { - char *text; /* for keys that correspond with text */ - KeySym keysym; /* for other keys, e.g. arrow keys */ - enum ledit_mode modes; /* modes in which this keybinding is functional */ + char *text; /* for keys that correspond with text */ + KeySym keysym; /* for other keys, e.g. arrow keys */ + enum ledit_mode modes; /* modes in which this keybinding is functional */ + enum key_type prev_keys; /* allowed previous keys */ enum key_type key_types; /* key types - used to determine if the key is allowed */ - void (*func)(void); /* callback function */ + void (*func)(void); /* callback function */ }; struct key_stack_elem { - enum key_type key; /* key type */ + enum key_type key; enum key_type followup; /* allowed keys to complete the keybinding */ - void (*func)(void); /* function to call if an allowed key is entered */ + /* callback function that motion commands call to complete a command - + * 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)(int line, int char_pos, enum key_type type); + int count; /* number of repetitions */ int data1; /* misc. data 1 */ int data2; /* misc. data 2 */ }; @@ -63,12 +71,7 @@ static ledit_buffer *buffer; /* TODO: protect against overflow, especially on repeating commands */ -static struct key_stack_elem *push_key_stack( - enum key_type key, - enum key_type followup, - void (*func)(void), - int data1, int data2 -); +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); void clear_key_stack(void); @@ -109,11 +112,81 @@ static void push_6(void); static void push_7(void); static void push_8(void); static void push_9(void); +static void key_d(void); +static void key_d_cb(int line, int char_pos, enum key_type type); static void change_keyboard(char *lang); static void key_press(XEvent event); static void +key_d(void) { + int num = 0; + struct key_stack_elem *e = pop_key_stack(); + if (e != NULL) { + if (e->key & KEY_NUMBER) { + num = e->count; + e = pop_key_stack(); + } + /* FIXME: checking equality of the function pointer may be a bit risky */ + if (e != NULL && e->motion_cb == &key_d_cb) { + int prevnum = e->count > 0 ? e->count : 1; + num = num > 0 ? num : 1; + int lines = num * prevnum; + + ledit_line *line = ledit_get_line(buffer, buffer->cur_line); + int x, softline; + pango_layout_index_to_line_x(line->layout, buffer->cur_index, 0, &softline, &x); + int softlines = pango_layout_get_line_count(line->layout); + if (softlines - softline >= lines) { + PangoLayoutLine *l = pango_layout_get_line_readonly(line->layout, softline + lines - 1); + e->motion_cb(buffer->cur_line, l->start_index, KEY_MOTION_LINE); + } else { + lines -= (softlines - softline); + int endline = buffer->cur_line + 1; + while (lines > 0 && endline < buffer->lines_num) { + line = ledit_get_line(buffer, endline); + softlines = pango_layout_get_line_count(line->layout); + lines -= softlines; + endline++; + } + endline--; + int endsoftline = 0; + if (lines <= 0) { + endsoftline = lines + softlines - 1; + } else { + endsoftline = softlines - 1; + } + PangoLayoutLine *l = pango_layout_get_line_readonly(line->layout, endsoftline); + e->motion_cb(endline, l->start_index, KEY_MOTION_LINE); + } + clear_key_stack(); + } else if (e != NULL) { + clear_key_stack(); + } + } + if (e == NULL) { + e = push_key_stack(); + e->key = KEY_MOTION; /* ? */ + e->count = num; + e->motion_cb = &key_d_cb; + } +} + +/* FIXME: should this get number of lines to remove or actual end line? */ +static void +key_d_cb(int line, int char_pos, enum key_type type) { + printf("%d, %d\n", line, char_pos); + int line_based = type == KEY_MOTION_LINE ? 1 : 0; + ledit_delete_range( + buffer, line_based, + buffer->cur_line, buffer->cur_index, + line, char_pos, + &buffer->cur_line, &buffer->cur_index + ); + ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index); +} + +static void key_x(void) { struct key_stack_elem *e = pop_key_stack(); int num = 1; @@ -121,7 +194,7 @@ key_x(void) { if (e && !(e->key & KEY_NUMBER)) return; if (e) - num = e->data1; + num = e->count; if (num <= 0) num = 1; printf("delete %d\n", num); @@ -130,14 +203,16 @@ key_x(void) { static void push_num(int num) { struct key_stack_elem *e = peek_key_stack(); - if (!e || !(e->key & KEY_NUMBER)) - e = push_key_stack(KEY_NUMBER, KEY_NUMBERALLOWED, NULL, 0, 0); + if (!e || !(e->key & KEY_NUMBER)) { + e = push_key_stack(); + e->key = KEY_NUMBER; + e->followup = KEY_NUMBER|KEY_NUMBERALLOWED; + } /* FIXME: error checking */ - e->data1 *= 10; - e->data1 += num; + e->count *= 10; + e->count += num; } -/* FIXME: CHANGE BEHAVIOR TO MOVEMENT */ static void push_0(void) { push_num(0); @@ -198,11 +273,7 @@ main(int argc, char *argv[]) { } static struct key_stack_elem * -push_key_stack( - enum key_type key, - enum key_type followup, - void (*func)(void), - int data1, int data2) { +push_key_stack(void) { struct key_stack_elem *e; if (key_stack.len >= key_stack.alloc) { size_t new_alloc = key_stack.alloc > 0 ? key_stack.alloc * 2 : 4; @@ -212,11 +283,12 @@ push_key_stack( key_stack.alloc = new_alloc; } e = &key_stack.stack[key_stack.len]; - e->key = key; - e->followup = followup; - e->func = func; - e->data1 = data1; - e->data2 = data2; + e->key = KEY_NONE; + e->followup = KEY_NONE; + e->motion_cb = NULL; + e->count = 0; + e->data1 = 0; + e->data2 = 0; key_stack.len++; return &key_stack.stack[key_stack.len - 1]; } @@ -757,21 +829,20 @@ move_cursor(int dir) { ledit_line *cur_line = ledit_get_line(buffer, buffer->cur_line); pango_layout_move_cursor_visually( cur_line->layout, TRUE, - buffer->cur_index, buffer->trailing, dir, - &buffer->cur_index, &buffer->trailing + buffer->cur_index, 0, dir, + &buffer->cur_index, &trailing ); /* FIXME: Allow cursor to be at end of soft line */ /* we don't currently support a difference between the cursor being at the end of a soft line and the beginning of the next line */ - /* - while (buffer->trailing > 0) { - buffer->trailing--; + /* FIXME: spaces at end of softlines are weird in normal mode */ + while (trailing > 0) { + trailing--; buffer->cur_index++; while (buffer->cur_index < cur_line->len && ((cur_line->text[buffer->cur_index] & 0xC0) == 0x80)) buffer->cur_index++; } - */ if (buffer->cur_index < 0) buffer->cur_index = 0; /* when in normal mode, the cursor cannot be at the very end @@ -832,23 +903,10 @@ enter_insert(void) { static void cursor_down(void) { - int lineno, x, trailing = 0; + int lineno, x; ledit_line *cur_line = ledit_get_line(buffer, buffer->cur_line); - pango_layout_index_to_line_x( - cur_line->layout, buffer->cur_index, 0, &lineno, &x - ); - PangoLayoutLine *cur_pango_line = - pango_layout_get_line_readonly(cur_line->layout, lineno); - if (cur_pango_line->resolved_dir == PANGO_DIRECTION_RTL) { - PangoRectangle rect; - pango_layout_line_get_extents(cur_pango_line, NULL, &rect); - x += (cur_line->w * PANGO_SCALE - rect.width); - } - if (state.mode == NORMAL) { - PangoRectangle pos; - pango_layout_index_to_pos(cur_line->layout, buffer->cur_index, &pos); - x += pos.width / 2; - } + ledit_pos_to_x_softline(cur_line, buffer->cur_index, &x, &lineno); + int maxlines = pango_layout_get_line_count(cur_line->layout); if (lineno == maxlines - 1) { ledit_wipe_line_cursor_attrs(buffer, buffer->cur_line); @@ -856,59 +914,21 @@ cursor_down(void) { if (buffer->cur_line < buffer->lines_num - 1) { buffer->cur_line++; cur_line = ledit_get_line(buffer, buffer->cur_line); - PangoLayoutLine *nextline = - pango_layout_get_line_readonly(cur_line->layout, 0); - if (nextline->resolved_dir == PANGO_DIRECTION_RTL) { - PangoRectangle rect; - pango_layout_line_get_extents(nextline, NULL, &rect); - x -= (cur_line->w * PANGO_SCALE - rect.width); - } - pango_layout_line_x_to_index( - nextline, x, &buffer->cur_index, &trailing - ); - if (state.mode == INSERT) - buffer->cur_index += trailing; + ledit_x_softline_to_pos(cur_line, x, 0, &buffer->cur_index); } } else { /* move to the next soft line */ - PangoLayoutLine *nextline = - pango_layout_get_line_readonly(cur_line->layout, lineno + 1); - if (nextline->resolved_dir == PANGO_DIRECTION_RTL) { - PangoRectangle rect; - pango_layout_line_get_extents(nextline, NULL, &rect); - x -= (cur_line->w * PANGO_SCALE - rect.width); - } - pango_layout_line_x_to_index( - nextline, x, &buffer->cur_index, &trailing - ); - if (state.mode == INSERT) - buffer->cur_index += trailing; + ledit_x_softline_to_pos(cur_line, x, lineno + 1, &buffer->cur_index); } ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index); } static void cursor_up(void) { - int lineno, x, trailing = 0; + int lineno, x; ledit_line *cur_line = ledit_get_line(buffer, buffer->cur_line); - pango_layout_index_to_line_x( - cur_line->layout, buffer->cur_index, 0, &lineno, &x - ); - PangoLayoutLine *cur_pango_line = - pango_layout_get_line_readonly(cur_line->layout, lineno); - /* FIXME: do these lines need to be unref'd? */ - if (cur_pango_line->resolved_dir == PANGO_DIRECTION_RTL) { - PangoRectangle rect; - pango_layout_line_get_extents(cur_pango_line, NULL, &rect); - /* FIXME: don't store w in each line because it is now the same for all */ - x += (cur_line->w * PANGO_SCALE - rect.width); - } - if (state.mode == NORMAL) { - PangoRectangle pos; - pango_layout_index_to_pos(cur_line->layout, buffer->cur_index, &pos); - x += pos.width / 2; - } - /* FIXME: clean this up (if and else are very similar) */ + ledit_pos_to_x_softline(cur_line, buffer->cur_index, &x, &lineno); + buffer->trailing = 0; if (lineno == 0) { ledit_wipe_line_cursor_attrs(buffer, buffer->cur_line); /* move to the previous hard line */ @@ -916,34 +936,11 @@ cursor_up(void) { buffer->cur_line--; cur_line = ledit_get_line(buffer, buffer->cur_line); int maxlines = pango_layout_get_line_count(cur_line->layout); - PangoLayoutLine *prevline = - pango_layout_get_line_readonly(cur_line->layout, maxlines - 1); - if (prevline->resolved_dir == PANGO_DIRECTION_RTL) { - PangoRectangle rect; - pango_layout_line_get_extents(prevline, NULL, &rect); - x -= (cur_line->w * PANGO_SCALE - rect.width); - } - pango_layout_line_x_to_index( - prevline, x, &buffer->cur_index, &trailing - ); - /* FIXME: also in visual? */ - if (state.mode == INSERT) - buffer->cur_index += trailing; + ledit_x_softline_to_pos(cur_line, x, maxlines - 1, &buffer->cur_index); } } else { /* move to the previous soft line */ - PangoLayoutLine *prevline = - pango_layout_get_line_readonly(cur_line->layout, lineno - 1); - if (prevline->resolved_dir == PANGO_DIRECTION_RTL) { - PangoRectangle rect; - pango_layout_line_get_extents(prevline, NULL, &rect); - x -= (cur_line->w * PANGO_SCALE - rect.width); - } - pango_layout_line_x_to_index( - prevline, x, &buffer->cur_index, &trailing - ); - if (state.mode == INSERT) - buffer->cur_index += trailing; + ledit_x_softline_to_pos(cur_line, x, lineno - 1, &buffer->cur_index); } ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index); } @@ -955,57 +952,66 @@ cursor_to_beginning(void) { } static struct key keys_en[] = { - {NULL, XK_BackSpace, INSERT, KEY_ANY, &backspace}, - {NULL, XK_Left, INSERT|NORMAL, KEY_ANY, &cursor_left}, - {NULL, XK_Right, INSERT|NORMAL, KEY_ANY, &cursor_right}, - {NULL, XK_Up, INSERT|NORMAL, KEY_ANY, &cursor_up}, - {NULL, XK_Down, INSERT|NORMAL, KEY_ANY, &cursor_down}, - {NULL, XK_Return, INSERT, KEY_ANY, &return_key}, - {NULL, XK_Delete, INSERT, KEY_ANY, &delete_key}, - {NULL, XK_Escape, INSERT, KEY_ANY, &escape_key}, - {"i", 0, NORMAL, KEY_ANY, &enter_insert}, - {"h", 0, NORMAL, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_left}, - {"l", 0, NORMAL, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_right}, - {"j", 0, NORMAL, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_down}, - {"k", 0, NORMAL, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_up}, - {"0", 0, NORMAL, KEY_ANY, &cursor_to_beginning}, - {"1", 0, NORMAL, KEY_NUMBER, &push_1}, - {"2", 0, NORMAL, KEY_NUMBER, &push_2}, - {"x", 0, NORMAL, KEY_NUMBERALLOWED, &key_x} + {NULL, XK_BackSpace, INSERT, KEY_ANY, KEY_ANY, &backspace}, + {NULL, XK_Left, INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_left}, + {NULL, XK_Right, INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_right}, + {NULL, XK_Up, INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_up}, + {NULL, XK_Down, INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_down}, + {NULL, XK_Return, INSERT, KEY_ANY, KEY_ANY, &return_key}, + {NULL, XK_Delete, INSERT, KEY_ANY, KEY_ANY, &delete_key}, + {NULL, XK_Escape, INSERT, KEY_ANY, KEY_ANY, &escape_key}, + {"i", 0, NORMAL, KEY_ANY, KEY_ANY, &enter_insert}, + {"h", 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_left}, + {"l", 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_right}, + {"j", 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_down}, + {"k", 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_up}, + {"0", 0, NORMAL, ~KEY_NUMBER, KEY_ANY, &cursor_to_beginning}, + {"0", 0, NORMAL, KEY_NUMBER, KEY_NUMBER, &push_0}, + {"1", 0, NORMAL, KEY_ANY, KEY_NUMBER, &push_1}, + {"2", 0, NORMAL, KEY_ANY, KEY_NUMBER, &push_2}, + {"3", 0, NORMAL, KEY_ANY, KEY_NUMBER, &push_3}, + {"4", 0, NORMAL, KEY_ANY, KEY_NUMBER, &push_4}, + {"5", 0, NORMAL, KEY_ANY, KEY_NUMBER, &push_5}, + {"6", 0, NORMAL, KEY_ANY, KEY_NUMBER, &push_6}, + {"7", 0, NORMAL, KEY_ANY, KEY_NUMBER, &push_7}, + {"8", 0, NORMAL, KEY_ANY, KEY_NUMBER, &push_8}, + {"9", 0, NORMAL, KEY_ANY, KEY_NUMBER, &push_9}, + {"x", 0, NORMAL, KEY_ANY, KEY_NUMBERALLOWED, &key_x}, + {"d", 0, NORMAL, KEY_ANY, KEY_MOTION|KEY_NUMBERALLOWED, &key_d} }; static struct key keys_ur[] = { - {NULL, XK_BackSpace, INSERT, KEY_ANY, &backspace}, - {NULL, XK_Left, INSERT|NORMAL, KEY_ANY, &cursor_left}, - {NULL, XK_Right, INSERT|NORMAL, KEY_ANY, &cursor_right}, - {NULL, XK_Up, INSERT|NORMAL, KEY_ANY, &cursor_up}, - {NULL, XK_Down, INSERT|NORMAL, KEY_ANY, &cursor_down}, - {NULL, XK_Return, INSERT, KEY_ANY, &return_key}, - {NULL, XK_Delete, INSERT, KEY_ANY, &delete_key}, - {NULL, XK_Escape, INSERT, KEY_ANY, &escape_key}, - {"ی", 0, NORMAL, KEY_ANY, &enter_insert}, - {"ح", 0, NORMAL, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_left}, - {"ل", 0, NORMAL, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_right}, - {"ج", 0, NORMAL, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_down}, - {"ک", 0, NORMAL, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_up}, - {"0", 0, NORMAL, KEY_ANY, &cursor_to_beginning} + {NULL, XK_BackSpace, INSERT, KEY_ANY, KEY_ANY, &backspace}, + {NULL, XK_Left, INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_left}, + {NULL, XK_Right, INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_right}, + {NULL, XK_Up, INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_up}, + {NULL, XK_Down, INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_down}, + {NULL, XK_Return, INSERT, KEY_ANY, KEY_ANY, &return_key}, + {NULL, XK_Delete, INSERT, KEY_ANY, KEY_ANY, &delete_key}, + {NULL, XK_Escape, INSERT, KEY_ANY, KEY_ANY, &escape_key}, + {"ی", 0, NORMAL, KEY_ANY, KEY_ANY, &enter_insert}, + {"ح", 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_left}, + {"ل", 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_right}, + {"ج", 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_down}, + {"ک", 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_up}, + {"0", 0, NORMAL, KEY_ANY, KEY_ANY, &cursor_to_beginning} }; static struct key keys_hi[] = { - {NULL, XK_BackSpace, INSERT, KEY_ANY, &backspace}, - {NULL, XK_Left, INSERT|NORMAL, KEY_ANY, &cursor_left}, - {NULL, XK_Right, INSERT|NORMAL, KEY_ANY, &cursor_right}, - {NULL, XK_Up, INSERT|NORMAL, KEY_ANY, &cursor_up}, - {NULL, XK_Down, INSERT|NORMAL, KEY_ANY, &cursor_down}, - {NULL, XK_Return, INSERT, KEY_ANY, &return_key}, - {NULL, XK_Delete, INSERT, KEY_ANY, &delete_key}, - {NULL, XK_Escape, INSERT, KEY_ANY, &escape_key}, - {"ि", 0, NORMAL, KEY_ANY, &enter_insert}, - {"ह", 0, NORMAL, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_left}, - {"ल", 0, NORMAL, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_right}, - {"ज", 0, NORMAL, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_down}, - {"क", 0, NORMAL, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_up}, - {"0", 0, NORMAL, KEY_ANY, &cursor_to_beginning} + {NULL, XK_BackSpace, INSERT, KEY_ANY, KEY_ANY, &backspace}, + {NULL, XK_Left, INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_left}, + {NULL, XK_Right, INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_right}, + {NULL, XK_Up, INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_up}, + {NULL, XK_Down, INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_down}, + {NULL, XK_Return, INSERT, KEY_ANY, KEY_ANY, &return_key}, + {NULL, XK_Delete, INSERT, KEY_ANY, KEY_ANY, &delete_key}, + {NULL, XK_Escape, INSERT, KEY_ANY, KEY_ANY, &escape_key}, + {"ि", 0, NORMAL, KEY_ANY, KEY_ANY, &enter_insert}, + {"ह", 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_left}, + {"ल", 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_right}, + {"ज", 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_down}, + {"क", 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_up}, + {"0", 0, NORMAL, KEY_ANY, KEY_ANY, &cursor_to_beginning} }; #define LENGTH(X) (sizeof(X) / sizeof(X[0])) @@ -1044,10 +1050,12 @@ key_press(XEvent event) { state.xic, &event.xkey, buf, sizeof(buf), &sym, NULL ); int found = 0; + struct key_stack_elem *e = peek_key_stack(); for (int i = 0; i < cur_keys->num_keys; i++) { if (cur_keys->keys[i].text) { if (n > 0 && (cur_keys->keys[i].modes & state.mode) && + (!e || (e->key & cur_keys->keys[i].prev_keys)) && !strncmp(cur_keys->keys[i].text, buf, n)) { cur_keys->keys[i].func(); found = 1; @@ -1057,6 +1065,8 @@ key_press(XEvent event) { cur_keys->keys[i].func(); found = 1; } + if (found) + break; } if (state.mode == INSERT && !found && n > 0) { ledit_insert_text(