tAdd initial support for deletion - 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 <[email protected]> | |
Date: Wed, 12 May 2021 21:47:17 +0200 | |
Add initial support for deletion | |
Diffstat: | |
M buffer.c | 275 +++++++++++++++++++++++++++++… | |
M buffer.h | 10 ++++++++++ | |
M ledit.c | 342 ++++++++++++++++-------------… | |
3 files changed, 452 insertions(+), 175 deletions(-) | |
--- | |
diff --git a/buffer.c b/buffer.c | |
t@@ -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_S… | |
- 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; | |
t@@ -185,18 +186,27 @@ ledit_append_line(ledit_buffer *buffer, int line_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 * | |
t@@ -270,3 +280,250 @@ ledit_delete_unicode_char(ledit_buffer *buffer, int line… | |
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 - le… | |
+ 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_r… | |
+ 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->lay… | |
+ /* 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_index… | |
+ int l1 = softline1 < softline2 ? softline1 : softline2; | |
+ int l2 = softline1 < softline2 ? softline2 : softline1; | |
+ int softlines = pango_layout_get_line_count(line1->lay… | |
+ PangoLayoutLine *pl1 = pango_layout_get_line_readonly(… | |
+ PangoLayoutLine *pl2 = pango_layout_get_line_readonly(… | |
+ /* don't delete entire line of it is the last one rema… | |
+ if (l1 == 0 && l2 == softlines - 1 && buffer->lines_nu… | |
+ ledit_delete_line_entry(buffer, line_index1); | |
+ /* note: line_index1 is now the index of the n… | |
+ 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 … | |
+ buffer->lines_num > 1 && line_index… | |
+ *new_line_ret = line_index1 - 1; | |
+ ledit_line *prevline = ledit_get_line(… | |
+ softlines = pango_layout_get_line_coun… | |
+ ledit_x_softline_to_pos(prevline, x, s… | |
+ } | |
+ } else { | |
+ /* FIXME: sanity checks that the length is act… | |
+ delete_line_section( | |
+ buffer, line_index1, pl1->start_index, | |
+ pl2->start_index + pl2->length - pl1->star… | |
+ ); | |
+ if (l2 == softlines - 1 && line_index1 < buffe… | |
+ *new_line_ret = line_index1 + 1; | |
+ ledit_x_softline_to_pos( | |
+ ledit_get_line(buffer, line_index1… | |
+ 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 th… | |
+ *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,… | |
+ pango_layout_index_to_line_x(ll2->layout, b2, 0, &sl2,… | |
+ PangoLayoutLine *pl1 = pango_layout_get_line_readonly(… | |
+ PangoLayoutLine *pl2 = pango_layout_get_line_readonly(… | |
+ int softlines = pango_layout_get_line_count(ll2->layou… | |
+ if (sl1 == 0 && sl2 == softlines - 1) { | |
+ if (l1 == 0 && l2 == buffer->lines_num - 1) { | |
+ delete_line_section(buffer, l1, 0, ll1… | |
+ ledit_delete_line_entries(buffer, l1 +… | |
+ *new_line_ret = 0; | |
+ *new_byte_ret = 0; | |
+ } else { | |
+ ledit_delete_line_entries(buffer, l1, … | |
+ if (l1 >= buffer->lines_num) { | |
+ *new_line_ret = buffer->lines_… | |
+ ledit_line *new_lline = ledit_… | |
+ int new_softlines = pango_layo… | |
+ ledit_x_softline_to_pos(new_ll… | |
+ } 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_… | |
+ ledit_delete_line_entries(buffer, l1, l2 - 1); | |
+ *new_line_ret = l1; | |
+ ledit_x_softline_to_pos(ledit_get_line(buffer,… | |
+ } else if (sl2 == softlines - 1) { | |
+ delete_line_section(buffer, l1, pl1->start_ind… | |
+ 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… | |
+ int new_softlines = pango_layout_get_l… | |
+ ledit_x_softline_to_pos(new_lline, x, … | |
+ } 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_ind… | |
+ delete_line_section(buffer, l2, 0, pl2->start_… | |
+ if (l2 > l1 + 1) | |
+ ledit_delete_line_entries(buffer, l1 +… | |
+ *new_line_ret = l1 + 1; | |
+ ledit_x_softline_to_pos(ledit_get_line(buffer,… | |
+ } | |
+ } | |
+ } 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 | |
t@@ -1,5 +1,6 @@ | |
typedef struct ledit_buffer ledit_buffer; | |
+/* FIXME: size_t for len, etc. */ | |
typedef struct { | |
PangoLayout *layout; | |
char *text; | |
t@@ -36,7 +37,16 @@ void ledit_wipe_line_cursor_attrs(ledit_buffer *buffer, int… | |
void ledit_insert_text(ledit_buffer *buffer, int line_index, int index, char *… | |
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_i… | |
+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 *softl… | |
+void ledit_x_softline_to_pos(ledit_line *line, int x, int softline, int *pos_r… | |
diff --git a/ledit.c b/ledit.c | |
t@@ -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 function… | |
+ enum key_type prev_keys; /* allowed previous keys */ | |
enum key_type key_types; /* key types - used to determine if the key i… | |
- 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 sta… | |
+ * 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 */ | |
}; | |
t@@ -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); | |
t@@ -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 b… | |
+ 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_… | |
+ int x, softline; | |
+ pango_layout_index_to_line_x(line->layout, buffer->cur… | |
+ int softlines = pango_layout_get_line_count(line->layo… | |
+ if (softlines - softline >= lines) { | |
+ PangoLayoutLine *l = pango_layout_get_line_rea… | |
+ e->motion_cb(buffer->cur_line, l->start_index,… | |
+ } else { | |
+ lines -= (softlines - softline); | |
+ int endline = buffer->cur_line + 1; | |
+ while (lines > 0 && endline < buffer->lines_nu… | |
+ line = ledit_get_line(buffer, endline); | |
+ softlines = pango_layout_get_line_coun… | |
+ lines -= softlines; | |
+ endline++; | |
+ } | |
+ endline--; | |
+ int endsoftline = 0; | |
+ if (lines <= 0) { | |
+ endsoftline = lines + softlines - 1; | |
+ } else { | |
+ endsoftline = softlines - 1; | |
+ } | |
+ PangoLayoutLine *l = pango_layout_get_line_rea… | |
+ e->motion_cb(endline, l->start_index, KEY_MOTI… | |
+ } | |
+ 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_inde… | |
+} | |
+ | |
+static void | |
key_x(void) { | |
struct key_stack_elem *e = pop_key_stack(); | |
int num = 1; | |
t@@ -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); | |
t@@ -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); | |
t@@ -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 :… | |
t@@ -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]; | |
} | |
t@@ -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 | |
t@@ -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,… | |
- 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); | |
t@@ -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, … | |
- 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_i… | |
} | |
} else { | |
/* move to the next soft line */ | |
- PangoLayoutLine *nextline = | |
- pango_layout_get_line_readonly(cur_line->layout, lineno + … | |
- 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_… | |
} | |
ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_inde… | |
} | |
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 sam… | |
- 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,… | |
- 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 */ | |
t@@ -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->l… | |
- PangoLayoutLine *prevline = | |
- pango_layout_get_line_readonly(cur_line->layout, m… | |
- if (prevline->resolved_dir == PANGO_DIRECTION_RTL) { | |
- PangoRectangle rect; | |
- pango_layout_line_get_extents(prevline, NULL, … | |
- 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, &bu… | |
} | |
} else { | |
/* move to the previous soft line */ | |
- PangoLayoutLine *prevline = | |
- pango_layout_get_line_readonly(cur_line->layout, lineno - … | |
- 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_… | |
} | |
ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_inde… | |
} | |
t@@ -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_lef… | |
+ {"l", 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_rig… | |
+ {"j", 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_dow… | |
+ {"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_le… | |
+ {"ل", 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_ri… | |
+ {"ج", 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_do… | |
+ {"ک", 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_l… | |
+ {"ल", 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_r… | |
+ {"ज", 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_d… | |
+ {"क", 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_u… | |
+ {"0", 0, NORMAL, KEY_ANY, KEY_ANY, &cursor_to_beginning} | |
}; | |
#define LENGTH(X) (sizeof(X) / sizeof(X[0])) | |
t@@ -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; | |
t@@ -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( |