commit 3895a14f2fb3bcc35ba842e259074a67adddb8a1
parent 52cea73bf0e43483210cb94e5177989bc524a30c
Author: lumidify <nobody@lumidify.org>
Date:   Thu, 13 May 2021 21:54:24 +0200
Add basic repetition and motion callback support to cursor movement keys
Diffstat:
| M | buffer.c |  |  | 12 | +++++++++--- | 
| M | ledit.c |  |  | 249 | +++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------- | 
2 files changed, 171 insertions(+), 90 deletions(-)
diff --git a/buffer.c b/buffer.c
@@ -494,10 +494,16 @@ ledit_delete_range(
 			}
 			delete_line_section(buffer, line_index1, b1, b2 - b1);
 			*new_line_ret = line_index1;
+			ledit_line *ll = ledit_get_line(buffer, line_index1);
+			/* FIXME: where should this be done? */
+			/* FIXME: also do this below in the else statement */
+			if (buffer->state->mode == NORMAL && b1 == ll->len && b1 > 0) {
+				/* FIXME: use grapheme instead of character! */
+				b1 = ll->len - 1;
+				while (b1 > 0 && ((ll->text[b1] & 0xC0) == 0x80))
+					b1--;
+			}
 			*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) {
diff --git a/ledit.c b/ledit.c
@@ -91,9 +91,10 @@ static void resize_window(int w, int h);
 
 static void backspace(void);
 static void delete_key(void);
-static void move_cursor(int dir);
+static void move_cursor_left_right(int dir);
 static void cursor_left(void);
 static void cursor_right(void);
+static void move_cursor_up_down(int dir);
 static void cursor_down(void);
 static void cursor_up(void);
 static void cursor_to_beginning(void);
@@ -117,6 +118,67 @@ 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 get_new_line_softline(
+    int cur_line, int cur_index, int movement,
+    int *new_line_ret, int *new_softline_ret
+);
+
+static void
+get_new_line_softline(
+    int cur_line, int cur_index, int movement,
+    int *new_line_ret, int *new_softline_ret) {
+	ledit_line *line = ledit_get_line(buffer, cur_line);
+	int x, softline;
+	pango_layout_index_to_line_x(line->layout, cur_index, 0, &softline, &x);
+	if (movement > 0) {
+		int softlines = pango_layout_get_line_count(line->layout);
+		if (softlines - softline > movement) {
+			*new_line_ret = cur_line;
+			*new_softline_ret = softline + movement;
+		} else {
+			movement -= (softlines - softline - 1);
+			int endline = cur_line + 1;
+			while (movement > 0 && endline < buffer->lines_num) {
+				line = ledit_get_line(buffer, endline);
+				softlines = pango_layout_get_line_count(line->layout);
+				movement -= softlines;
+				endline++;
+			}
+			endline--;
+			if (movement <= 0) {
+				*new_softline_ret = movement + softlines - 1;
+			} else {
+				*new_softline_ret = softlines - 1;
+			}
+			*new_line_ret = endline;
+		}
+	} else if (movement < 0) {
+		int softlines = 0;
+		if (softline + movement >= 0) {
+			*new_line_ret = cur_line;
+			*new_softline_ret = softline + movement;
+		} else {
+			movement += softline;
+			int endline = cur_line - 1;
+			while (movement < 0 && endline >= 0) {
+				line = ledit_get_line(buffer, endline);
+				softlines = pango_layout_get_line_count(line->layout);
+				movement += softlines;
+				endline--;
+			}
+			endline++;
+			if (movement >= 0) {
+				*new_softline_ret = movement;
+			} else {
+				*new_softline_ret = 0;
+			}
+			*new_line_ret = endline;
+		}
+	} else {
+		*new_line_ret = cur_line;
+		*new_softline_ret = softline;
+	}
+}
 
 static void
 key_d(void) {
@@ -132,33 +194,14 @@ key_d(void) {
 			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);
-			}
+			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();
@@ -175,7 +218,6 @@ key_d(void) {
 /* 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,
@@ -819,51 +861,79 @@ delete_key(void) {
 	ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
 }
 
-/* FIXME: this comment needs to be implemented still */
-/* num is number of graphemes to move (see pango documentation) - negative
- * is left, positive is right */
 static void
-move_cursor(int dir) {
+move_cursor_left_right(int dir) {
+	int num = 1;
+	struct key_stack_elem *e = pop_key_stack();
+	if (e != NULL) {
+		if (e->key & KEY_NUMBER) {
+			num = e->count > 0 ? e->count : 0;
+			e = pop_key_stack();
+		}
+		if (e != NULL)
+			num *= (e->count > 0 ? e->count : 1);
+	}
+
 	/* FIXME: trailing */
-	int trailing, last_index = buffer->cur_index;
+	int trailing = 0, tmp_index;
 	ledit_line *cur_line = ledit_get_line(buffer, buffer->cur_line);
-	pango_layout_move_cursor_visually(
-	    cur_line->layout, TRUE,
-	    buffer->cur_index, 0, dir,
-	    &buffer->cur_index, &trailing
-	);
+	int new_index = buffer->cur_index, last_index = buffer->cur_index;
+	while (num > 0) {
+		tmp_index = new_index;
+		pango_layout_move_cursor_visually(
+		    cur_line->layout, TRUE,
+		    new_index, trailing, dir,
+		    &new_index, &trailing
+		);
+		/* for some reason, this is necessary */
+		if (new_index < 0)
+			new_index = 0;
+		else if (new_index > cur_line->len)
+			new_index = cur_line->len;
+		num--;
+		if (tmp_index != new_index)
+			last_index = tmp_index;
+	}
 	/* 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 */
 	/* 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++;
+		new_index++;
+		while (new_index < cur_line->len &&
+		       ((cur_line->text[new_index] & 0xC0) == 0x80))
+			new_index++;
 	}
-	if (buffer->cur_index < 0)
-		buffer->cur_index = 0;
+	if (new_index < 0)
+		new_index = 0;
 	/* when in normal mode, the cursor cannot be at the very end
 	   of the line because it's always covering a character */
-	if (buffer->cur_index >= cur_line->len) {
-		if (state.mode == NORMAL)
-			buffer->cur_index = last_index;
-		else
-			buffer->cur_index = cur_line->len;
+	if (new_index >= cur_line->len) {
+		if (state.mode == NORMAL && (e == NULL || e->motion_cb == NULL)) {
+			new_index = last_index;
+		} else {
+			/* FIXME: I guess this is unnecessary */
+			new_index = cur_line->len;
+		}
 	}
-	ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
+	if (e != NULL & e->motion_cb != NULL) {
+		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);
+	}
+	clear_key_stack();
 }
 
 static void
 cursor_left(void) {
-	move_cursor(-1);
+	move_cursor_left_right(-1);
 }
 
 static void
 cursor_right(void) {
-	move_cursor(1);
+	move_cursor_left_right(1);
 }
 
 static void
@@ -901,48 +971,53 @@ enter_insert(void) {
 	ledit_wipe_line_cursor_attrs(buffer, buffer->cur_line);
 }
 
+/* FIXME: Check if previous key allows motion command - or is this checked automatically before? */
 static void
-cursor_down(void) {
-	int lineno, x;
-	ledit_line *cur_line = ledit_get_line(buffer, buffer->cur_line);
-	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);
-		/* move to the next hard line */
-		if (buffer->cur_line < buffer->lines_num - 1) {
-			buffer->cur_line++;
-			cur_line = ledit_get_line(buffer, buffer->cur_line);
-			ledit_x_softline_to_pos(cur_line, x, 0, &buffer->cur_index);
+move_cursor_up_down(int dir) {
+	int new_line, new_softline;
+
+	int num = 1;
+	struct key_stack_elem *e = pop_key_stack();
+	if (e != NULL) {
+		if (e->key & KEY_NUMBER) {
+			num = e->count > 0 ? e->count : 0;
+			e = pop_key_stack();
 		}
+		if (e != NULL)
+			num *= (e->count > 0 ? e->count : 1);
+	}
+	num *= dir;
+
+	get_new_line_softline(
+	    buffer->cur_line, buffer->cur_index, num,
+	    &new_line, &new_softline
+	);
+
+	ledit_line *cur_lline = ledit_get_line(buffer, buffer->cur_line);
+	ledit_line *new_lline = ledit_get_line(buffer, new_line);
+	if (e != NULL && e->motion_cb != NULL) {
+		PangoLayoutLine *pl = pango_layout_get_line_readonly(new_lline->layout, new_softline);
+		e->motion_cb(new_line, pl->start_index, KEY_MOTION_LINE);
 	} else {
-		/* move to the next soft line */
-		ledit_x_softline_to_pos(cur_line, x, lineno + 1, &buffer->cur_index);
+		int lineno, x;
+		ledit_pos_to_x_softline(cur_lline, buffer->cur_index, &x, &lineno);
+		ledit_x_softline_to_pos(new_lline, x, new_softline, &buffer->cur_index);
+		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);
 	}
-	ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
+	clear_key_stack();
+}
+
+static void
+cursor_down(void) {
+	move_cursor_up_down(1);
 }
 
 static void
 cursor_up(void) {
-	int lineno, x;
-	ledit_line *cur_line = ledit_get_line(buffer, buffer->cur_line);
-	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 */
-		if (buffer->cur_line > 0) {
-			buffer->cur_line--;
-			cur_line = ledit_get_line(buffer, buffer->cur_line);
-			int maxlines = pango_layout_get_line_count(cur_line->layout);
-			ledit_x_softline_to_pos(cur_line, x, maxlines - 1, &buffer->cur_index);
-		}
-	} else {
-		/* move to the previous soft line */
-		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);
+	move_cursor_up_down(-1);
 }
 
 static void