tAdd visual mode and make current keys work with it - 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 413b7e3a74968e128ede783a25145dc5aea70c99 | |
parent 10a6b45de3f15406bb82817a1bf28c79d492145f | |
Author: lumidify <[email protected]> | |
Date: Sun, 16 May 2021 20:26:05 +0200 | |
Add visual mode and make current keys work with it | |
Diffstat: | |
M buffer.c | 45 +++++++++++++++++------------… | |
M buffer.h | 1 + | |
M ledit.c | 240 ++++++++++++++++++++++-------… | |
3 files changed, 195 insertions(+), 91 deletions(-) | |
--- | |
diff --git a/buffer.c b/buffer.c | |
t@@ -360,7 +360,29 @@ ledit_x_softline_to_pos(ledit_line *line, int x, int soft… | |
} | |
} | |
-/* FIXME: cursor jumps weirdly */ | |
+int | |
+ledit_get_legal_normal_pos(ledit_buffer *buffer, int line, int pos) { | |
+ /* move back one grapheme if at end of line */ | |
+ int ret = pos; | |
+ ledit_line *final_line = ledit_get_line(buffer, line); | |
+ if (pos == final_line->len && pos > 0) { | |
+ int nattrs; | |
+ const PangoLogAttr *attrs = | |
+ pango_layout_get_log_attrs_readonly(final_line->layout, &n… | |
+ int cur = nattrs - 2; | |
+ ret--; | |
+ while (ret > 0 && ((final_line->text[ret] & 0xC0) == 0x80)) | |
+ ret--; | |
+ while (ret > 0 && cur > 0 && !attrs[cur].is_cursor_position) { | |
+ cur--; | |
+ ret--; | |
+ while (ret > 0 && ((final_line->text[ret] & 0xC0) == 0… | |
+ ret--; | |
+ } | |
+ } | |
+ return ret; | |
+} | |
+ | |
/* FIXME: use at least somewhat sensible variable names */ | |
void | |
ledit_delete_range( | |
t@@ -541,24 +563,7 @@ ledit_delete_range( | |
*new_byte_ret = b1; | |
ledit_delete_line_entries(buffer, l1 + 1, l2); | |
} | |
- /* move back one grapheme if at end of line and in normal mode… | |
- ledit_line *final_line = ledit_get_line(buffer, *new_line_ret); | |
- if (buffer->state->mode == NORMAL && | |
- *new_byte_ret == final_line->len && | |
- *new_byte_ret > 0) { | |
- int nattrs; | |
- const PangoLogAttr *attrs = | |
- pango_layout_get_log_attrs_readonly(final_line->la… | |
- int cur = nattrs - 2; | |
- (*new_byte_ret)--; | |
- while (*new_byte_ret > 0 && ((final_line->text[*new_by… | |
- (*new_byte_ret)--; | |
- while (*new_byte_ret > 0 && cur > 0 && !attrs[cur].is_… | |
- cur--; | |
- (*new_byte_ret)--; | |
- while (*new_byte_ret > 0 && ((final_line->text… | |
- (*new_byte_ret)--; | |
- } | |
- } | |
+ if (buffer->state->mode == NORMAL) | |
+ *new_byte_ret = ledit_get_legal_normal_pos(buffer, *ne… | |
} | |
} | |
diff --git a/buffer.h b/buffer.h | |
t@@ -51,6 +51,7 @@ 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… | |
+int ledit_get_legal_normal_pos(ledit_buffer *buffer, int line, int pos); | |
void ledit_delete_range( | |
ledit_buffer *buffer, int line_based, | |
int line_index1, int byte_index1, | |
diff --git a/ledit.c b/ledit.c | |
t@@ -180,39 +180,63 @@ get_new_line_softline( | |
} | |
} | |
+static int | |
+delete_selection(void) { | |
+ if (buffer->sel.line1 != buffer->sel.line2 || buffer->sel.byte1 != buf… | |
+ ledit_delete_range( | |
+ buffer, 0, | |
+ buffer->sel.line1, buffer->sel.byte1, | |
+ buffer->sel.line2, buffer->sel.byte2, | |
+ &buffer->cur_line, &buffer->cur_index | |
+ ); | |
+ buffer->sel.line1 = buffer->sel.line2 = -1; | |
+ buffer->sel.byte1 = buffer->sel.byte2 = -1; | |
+ ledit_wipe_line_cursor_attrs(buffer, buffer->cur_line); | |
+ return 1; | |
+ } | |
+ return 0; | |
+} | |
+ | |
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(); | |
+ if (delete_selection()) { | |
+ state.mode = NORMAL; | |
+ buffer->cur_index = ledit_get_legal_normal_pos(buffer, buffer-… | |
+ ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->… | |
+ clear_key_stack(); | |
+ } else { | |
+ 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 ma… | |
+ 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; | |
+ int new_line, new_softline; | |
+ get_new_line_softline( | |
+ buffer->cur_line, buffer->cur_index, lines… | |
+ &new_line, &new_softline | |
+ ); | |
+ ledit_line *ll = ledit_get_line(buffer, new_li… | |
+ PangoLayoutLine *pl = pango_layout_get_line_re… | |
+ e->motion_cb(new_line, pl->start_index, KEY_MO… | |
+ clear_key_stack(); | |
+ } else if (e != NULL) { | |
+ clear_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; | |
- int new_line, new_softline; | |
- get_new_line_softline( | |
- buffer->cur_line, buffer->cur_index, lines - 1, | |
- &new_line, &new_softline | |
- ); | |
- ledit_line *ll = ledit_get_line(buffer, new_line); | |
- PangoLayoutLine *pl = pango_layout_get_line_readonly(l… | |
- e->motion_cb(new_line, pl->start_index, KEY_MOTION_LIN… | |
- 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; | |
} | |
} | |
- 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? */ | |
t@@ -239,7 +263,6 @@ key_x(void) { | |
num = e->count; | |
if (num <= 0) | |
num = 1; | |
- printf("delete %d\n", num); | |
} | |
static void | |
t@@ -680,7 +703,7 @@ redraw(void) { | |
strong.x / PANGO_SCALE, cursor_y, | |
10, strong.height / PANGO_SCALE | |
); | |
- } else if (state.mode == INSERT) { | |
+ } else if (state.mode == INSERT || state.mode == VISUAL) { | |
XDrawLine( | |
state.dpy, state.drawable, state.gc, | |
strong.x / PANGO_SCALE, cursor_y, | |
t@@ -825,6 +848,12 @@ button_press(XEvent *event) { | |
int l, b; | |
xy_to_line_byte(x, y, &l, &b); | |
set_selection(l, b, l, b); | |
+ if (state.mode == NORMAL) { | |
+ ledit_wipe_line_cursor_attrs(buffer, b… | |
+ state.mode = VISUAL; | |
+ } | |
+ buffer->cur_line = l; | |
+ buffer->cur_index = b; | |
state.selecting = 1; | |
return 1; | |
} | |
t@@ -872,6 +901,8 @@ drag_motion(XEvent *event) { | |
int y = event->xbutton.y >= 0 ? event->xbutton.y : 0; | |
xy_to_line_byte(event->xbutton.x, y, &l, &b); | |
set_selection(buffer->sel.line1, buffer->sel.byte1, l, b); | |
+ buffer->cur_line = l; | |
+ buffer->cur_index = b; | |
return 1; | |
} | |
return 0; | |
t@@ -922,7 +953,9 @@ resize_window(int w, int h) { | |
static void | |
backspace(void) { | |
- if (buffer->cur_index == 0) { | |
+ if (delete_selection()) { | |
+ /* NOP */ | |
+ } else if (buffer->cur_index == 0) { | |
if (buffer->cur_line != 0) { | |
ledit_line *l1 = ledit_get_line(buffer, buffer->cur_li… | |
ledit_line *l2 = ledit_get_line(buffer, buffer->cur_li… | |
t@@ -946,7 +979,9 @@ backspace(void) { | |
static void | |
delete_key(void) { | |
ledit_line *cur_line = ledit_get_line(buffer, buffer->cur_line); | |
- if (buffer->cur_index == cur_line->len) { | |
+ if (delete_selection()) { | |
+ /* NOP */ | |
+ } else if (buffer->cur_index == cur_line->len) { | |
if (buffer->cur_line != buffer->lines_num - 1) { | |
ledit_line *next_line = ledit_get_line( | |
buffer, buffer->cur_line + 1 | |
t@@ -1027,7 +1062,15 @@ move_cursor_left_right(int dir) { | |
e->motion_cb(buffer->cur_line, new_index, KEY_MOTION_CHAR); | |
} else { | |
buffer->cur_index = new_index; | |
- ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->… | |
+ if (state.mode == VISUAL) { | |
+ set_selection(buffer->sel.line1, buffer->sel.byte1, bu… | |
+ } else if (state.mode == INSERT && | |
+ (buffer->sel.line1 != buffer->sel.line2 || | |
+ buffer->sel.byte1 != buffer->sel.byte2)) { | |
+ set_selection(buffer->cur_line, new_index, buffer->cur… | |
+ } else if (state.mode == NORMAL) { | |
+ ledit_set_line_cursor_attrs(buffer, buffer->cur_line, … | |
+ } | |
} | |
clear_key_stack(); | |
} | |
t@@ -1044,6 +1087,7 @@ cursor_right(void) { | |
static void | |
return_key(void) { | |
+ delete_selection(); | |
ledit_append_line(buffer, buffer->cur_line, buffer->cur_index); | |
/* FIXME: these aren't needed, right? This only works in insert mode | |
* anyways, so there's nothing to wipe */ | |
t@@ -1055,27 +1099,36 @@ return_key(void) { | |
static void | |
escape_key(void) { | |
- state.mode = NORMAL; | |
- clear_key_stack(); | |
- PangoDirection dir = PANGO_DIRECTION_RTL; | |
- int tmp_index = buffer->cur_index; | |
- ledit_line *cur_line = ledit_get_line(buffer, buffer->cur_line); | |
- if (buffer->cur_index >= cur_line->len) | |
- tmp_index--; | |
- if (tmp_index >= 0) | |
- dir = pango_layout_get_direction(cur_line->layout, tmp_index); | |
- if (dir == PANGO_DIRECTION_RTL || dir == PANGO_DIRECTION_WEAK_RTL) { | |
- cursor_right(); | |
+ clear_key_stack(); /* just in case... */ | |
+ if (state.mode == INSERT && | |
+ (buffer->sel.line1 != buffer->sel.line2 || | |
+ buffer->sel.byte1 != buffer->sel.byte2)) { | |
+ state.mode = VISUAL; | |
} else { | |
- cursor_left(); | |
+ state.mode = NORMAL; | |
+ clear_key_stack(); | |
+ PangoDirection dir = PANGO_DIRECTION_RTL; | |
+ int tmp_index = buffer->cur_index; | |
+ ledit_line *cur_line = ledit_get_line(buffer, buffer->cur_line… | |
+ if (buffer->cur_index >= cur_line->len) | |
+ tmp_index--; | |
+ if (tmp_index >= 0) | |
+ dir = pango_layout_get_direction(cur_line->layout, tmp… | |
+ if (dir == PANGO_DIRECTION_RTL || dir == PANGO_DIRECTION_WEAK_… | |
+ cursor_right(); | |
+ } else { | |
+ cursor_left(); | |
+ } | |
+ ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->… | |
} | |
- ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_inde… | |
} | |
static void | |
enter_insert(void) { | |
+ if (state.mode == NORMAL) | |
+ ledit_wipe_line_cursor_attrs(buffer, buffer->cur_line); | |
state.mode = INSERT; | |
- ledit_wipe_line_cursor_attrs(buffer, buffer->cur_line); | |
+ clear_key_stack(); | |
} | |
/* FIXME: Check if previous key allows motion command - or is this checked aut… | |
t@@ -1112,7 +1165,16 @@ move_cursor_up_down(int dir) { | |
if (buffer->cur_line != new_line) | |
ledit_wipe_line_cursor_attrs(buffer, buffer->cur_line); | |
buffer->cur_line = new_line; | |
- ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->… | |
+ | |
+ if (state.mode == VISUAL) { | |
+ set_selection(buffer->sel.line1, buffer->sel.byte1, bu… | |
+ } else if (state.mode == INSERT && | |
+ (buffer->sel.line1 != buffer->sel.line2 || | |
+ buffer->sel.byte1 != buffer->sel.byte2)) { | |
+ set_selection(buffer->cur_line, buffer->cur_index, buf… | |
+ } else if (state.mode == NORMAL) { | |
+ ledit_set_line_cursor_attrs(buffer, buffer->cur_line, … | |
+ } | |
} | |
clear_key_stack(); | |
} | |
t@@ -1129,37 +1191,72 @@ cursor_up(void) { | |
static void | |
cursor_to_beginning(void) { | |
- buffer->cur_index = 0; | |
- ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_inde… | |
+ struct key_stack_elem *e = pop_key_stack(); | |
+ /* FIXME: error when no callback? */ | |
+ if (e != NULL & e->motion_cb != NULL) { | |
+ e->motion_cb(buffer->cur_line, 0, KEY_MOTION_CHAR); | |
+ } else { | |
+ buffer->cur_index = 0; | |
+ if (state.mode == VISUAL) { | |
+ set_selection( | |
+ buffer->sel.line1, buffer->sel.byte1, | |
+ buffer->cur_line, buffer->cur_index | |
+ ); | |
+ } else { | |
+ ledit_set_line_cursor_attrs( | |
+ buffer, buffer->cur_line, buffer->cur_index | |
+ ); | |
+ } | |
+ } | |
+ clear_key_stack(); | |
+} | |
+ | |
+static void | |
+enter_visual(void) { | |
+ state.mode = VISUAL; | |
+ buffer->sel.line1 = buffer->sel.line2 = buffer->cur_line; | |
+ buffer->sel.byte1 = buffer->sel.byte2 = buffer->cur_index; | |
+ ledit_wipe_line_cursor_attrs(buffer, buffer->cur_line); | |
+ clear_key_stack(); /* FIXME: error if not empty? */ | |
+} | |
+ | |
+static void | |
+switch_selection_end(void) { | |
+ swap(&buffer->sel.line1, &buffer->sel.line2); | |
+ swap(&buffer->sel.byte1, &buffer->sel.byte2); | |
+ buffer->cur_line = buffer->sel.line2; | |
+ buffer->cur_index = buffer->sel.byte2; | |
} | |
static struct key keys_en[] = { | |
{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_Left, VISUAL|INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_left}, | |
+ {NULL, XK_Right, VISUAL|INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_right… | |
+ {NULL, XK_Up, VISUAL|INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_up}, | |
+ {NULL, XK_Down, VISUAL|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}, | |
+ {NULL, XK_Escape, VISUAL|INSERT, KEY_ANY, KEY_ANY, &escape_key}, | |
+ {"i", 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &enter_insert}, | |
+ {"h", 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cur… | |
+ {"l", 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cur… | |
+ {"j", 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cur… | |
+ {"k", 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cur… | |
+ {"0", 0, NORMAL|VISUAL, ~KEY_NUMBER, KEY_ANY, &cursor_to_beginning}, | |
+ {"0", 0, NORMAL|VISUAL, KEY_NUMBER, KEY_NUMBER, &push_0}, | |
+ {"1", 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_1}, | |
+ {"2", 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_2}, | |
+ {"3", 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_3}, | |
+ {"4", 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_4}, | |
+ {"5", 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_5}, | |
+ {"6", 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_6}, | |
+ {"7", 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_7}, | |
+ {"8", 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_8}, | |
+ {"9", 0, NORMAL|VISUAL, 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} | |
+ {"d", 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION|KEY_NUMBERALLOWED, &key_d… | |
+ {"v", 0, NORMAL, KEY_ANY, KEY_ANY, &enter_visual}, | |
+ {"o", 0, VISUAL, KEY_ANY, KEY_ANY, &switch_selection_end}, | |
}; | |
static struct key keys_ur[] = { | |
t@@ -1251,6 +1348,7 @@ key_press(XEvent event) { | |
break; | |
} | |
if (state.mode == INSERT && !found && n > 0) { | |
+ delete_selection(); | |
ledit_insert_text( | |
buffer, buffer->cur_line, buffer->cur_index, buf, n | |
); |