commit 413b7e3a74968e128ede783a25145dc5aea70c99
parent 10a6b45de3f15406bb82817a1bf28c79d492145f
Author: lumidify <nobody@lumidify.org>
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
@@ -360,7 +360,29 @@ ledit_x_softline_to_pos(ledit_line *line, int x, int softline, int *pos_ret) {
}
}
-/* 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, &nattrs);
+ 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) == 0x80))
+ ret--;
+ }
+ }
+ return ret;
+}
+
/* FIXME: use at least somewhat sensible variable names */
void
ledit_delete_range(
@@ -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->layout, &nattrs);
- int cur = nattrs - 2;
- (*new_byte_ret)--;
- while (*new_byte_ret > 0 && ((final_line->text[*new_byte_ret] & 0xC0) == 0x80))
- (*new_byte_ret)--;
- while (*new_byte_ret > 0 && cur > 0 && !attrs[cur].is_cursor_position) {
- cur--;
- (*new_byte_ret)--;
- while (*new_byte_ret > 0 && ((final_line->text[*new_byte_ret] & 0xC0) == 0x80))
- (*new_byte_ret)--;
- }
- }
+ if (buffer->state->mode == NORMAL)
+ *new_byte_ret = ledit_get_legal_normal_pos(buffer, *new_line_ret, *new_byte_ret);
}
}
diff --git a/buffer.h b/buffer.h
@@ -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_index, int dir);
+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
@@ -180,39 +180,63 @@ get_new_line_softline(
}
}
+static int
+delete_selection(void) {
+ if (buffer->sel.line1 != buffer->sel.line2 || buffer->sel.byte1 != buffer->sel.byte2) {
+ 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->cur_line, buffer->cur_index);
+ ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
+ 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 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;
+ 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(ll->layout, new_softline);
+ e->motion_cb(new_line, pl->start_index, KEY_MOTION_LINE);
+ clear_key_stack();
+ } else if (e != NULL) {
+ clear_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;
- 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(ll->layout, new_softline);
- e->motion_cb(new_line, pl->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;
}
}
- 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? */
@@ -239,7 +263,6 @@ key_x(void) {
num = e->count;
if (num <= 0)
num = 1;
- printf("delete %d\n", num);
}
static void
@@ -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,
@@ -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, buffer->cur_line);
+ state.mode = VISUAL;
+ }
+ buffer->cur_line = l;
+ buffer->cur_index = b;
state.selecting = 1;
return 1;
}
@@ -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;
@@ -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_line - 1);
ledit_line *l2 = ledit_get_line(buffer, buffer->cur_line);
@@ -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
@@ -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->cur_index);
+ if (state.mode == VISUAL) {
+ set_selection(buffer->sel.line1, buffer->sel.byte1, buffer->sel.line2, new_index);
+ } 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_line, new_index);
+ } else if (state.mode == NORMAL) {
+ ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
+ }
}
clear_key_stack();
}
@@ -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 */
@@ -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_index);
+ if (dir == PANGO_DIRECTION_RTL || dir == PANGO_DIRECTION_WEAK_RTL) {
+ cursor_right();
+ } else {
+ cursor_left();
+ }
+ ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
}
- ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
}
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 automatically before? */
@@ -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->cur_index);
+
+ if (state.mode == VISUAL) {
+ set_selection(buffer->sel.line1, buffer->sel.byte1, buffer->cur_line, buffer->cur_index);
+ } 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, buffer->cur_line, buffer->cur_index);
+ } else if (state.mode == NORMAL) {
+ ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
+ }
}
clear_key_stack();
}
@@ -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_index);
+ 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_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},
+ {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, &cursor_left},
+ {"l", 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_right},
+ {"j", 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_down},
+ {"k", 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_up},
+ {"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[] = {
@@ -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
);