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 f9064673e6ced9b9d8a2c1461c108170c15a96bc
parent f46f28e4d372daa0353489d65a29078e69fe0376
Author: lumidify <nobody@lumidify.org>
Date:   Sun, 14 Nov 2021 21:50:14 +0100

Implement switching between hard line and soft line mode

But it's all really ugly and buggy.

Diffstat:
Mbuffer.c | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Mbuffer.h | 10++++++++--
Mkeys_basic.c | 291++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Mkeys_basic.h | 1+
Mkeys_basic_config.h | 3+++
Mledit.c | 1+
Mmemory.c | 17+++++++++++++++++
Mmemory.h | 1+
Mwindow.c | 21+++++++++++++++++----
Mwindow.h | 2++
10 files changed, 322 insertions(+), 110 deletions(-)

diff --git a/buffer.c b/buffer.c @@ -1,5 +1,6 @@ /* FIXME: shrink buffers when text length less than a fourth of the size */ /* FIXME: also cache PangoLayouts since keeping them around isn't really of much use? */ +/* FIXME: handle all undo within buffer to keep it consistent */ #include <stdio.h> #include <errno.h> @@ -1404,13 +1405,13 @@ ledit_buffer_get_legal_normal_pos(ledit_buffer *buffer, int line, int pos) { void ledit_buffer_delete_range( - ledit_buffer *buffer, int line_based, + ledit_buffer *buffer, enum delete_mode delmode, int line_index1, int byte_index1, int line_index2, int byte_index2, int *new_line_ret, int *new_byte_ret, ledit_range *final_range_ret, txtbuf *text_ret) { ledit_buffer_delete_range_base( - buffer, line_based, + buffer, delmode, line_index1, byte_index1, line_index2, byte_index2, new_line_ret, new_byte_ret, @@ -1423,10 +1424,15 @@ ledit_buffer_delete_range( } /* Note: line_index* and byte_index* don't need to be sorted */ +/* line_index1, byte_index1 are used as the cursor position in order + to determine the new cursor position */ /* FIXME: use at least somewhat sensible variable names */ +/* FIXME: I once noticed a bug where using 'dG' to delete to the end of + the file caused a line index way larger than buffer->lines_num to be + given, but I couldn't reproduce this bug */ void ledit_buffer_delete_range_base( - ledit_buffer *buffer, int line_based, + ledit_buffer *buffer, enum delete_mode delmode, int line_index1, int byte_index1, int line_index2, int byte_index2, int *new_line_ret, int *new_byte_ret, @@ -1435,9 +1441,80 @@ ledit_buffer_delete_range_base( /* range line x, range byte x */ int rgl1 = 0, rgb1 = 0, rgl2 = 0, rgb2 = 0; int new_line = 0, new_byte = 0; + assert(line_index1 >= 0); + assert(line_index2 >= 0); + assert(line_index1 < buffer->lines_num); + assert(line_index2 < buffer->lines_num); /* FIXME: could this be simplified by just calculating the range and then using the non-line-based version? */ - if (line_based) { + if (delmode == DELETE_HARDLINE) { + int x, sl_useless; + int l1 = line_index1, l2 = line_index2; + if (line_index1 > line_index2) { + l1 = line_index2; + l2 = line_index1; + } + int dell1 = l1, dell2 = l2; + ledit_line *ll = ledit_buffer_get_line(buffer, line_index1); + ledit_pos_to_x_softline(ll, byte_index1, &x, &sl_useless); + if (l1 > 0 && l2 < buffer->lines_num - 1) { + rgl1 = l1; + rgb1 = 0; + rgl2 = l2 + 1; + rgb2 = 0; + } else if (l1 > 0) { + rgl1 = l1 - 1; + ll = ledit_buffer_get_line(buffer, rgl1); + rgb1 = ll->len; + rgl2 = l2; + ll = ledit_buffer_get_line(buffer, rgl2); + rgb2 = ll->len; + } else if (l2 < buffer->lines_num - 1) { + rgl1 = l1; + rgb1 = 0; + rgl2 = l2 + 1; + rgb2 = 0; + } else { + rgl1 = l1; + rgb1 = 0; + rgl2 = l2; + ll = ledit_buffer_get_line(buffer, rgl2); + rgb2 = ll->len; + } + if (text_ret) { + ledit_buffer_copy_text_to_txtbuf( + buffer, text_ret, + rgl1, rgb1, rgl2, rgb2 + ); + } + /* default is dell1 = l1, dell2 = l2 */ + if (l2 < buffer->lines_num - 1) { + new_line = l1; + ledit_x_softline_to_pos( + ledit_buffer_get_line(buffer, l2 + 1), + x, 0, &new_byte + ); + } else if (l1 > 0) { + new_line = l1 - 1; + ledit_x_softline_to_pos( + ledit_buffer_get_line(buffer, l1 - 1), + x, 0, &new_byte + ); + } else { + dell1 = l1 + 1; + dell2 = l2; + new_line = l1; + new_byte = 0; + /* happens when all lines are deleted, so one line has to be cleared */ + ll = ledit_buffer_get_line(buffer, l1); + delete_line_section_base( + buffer, l1, 0, ll->len + ); + } + if (dell1 <= dell2) { + ledit_buffer_delete_line_entries_base(buffer, dell1, dell2); + } + } else if (delmode == DELETE_SOFTLINE) { int x, softline1, softline2; ledit_line *line1 = ledit_buffer_get_line(buffer, line_index1); normalize_and_set_pango_text(line1); diff --git a/buffer.h b/buffer.h @@ -53,6 +53,12 @@ struct ledit_buffer { ledit_buffer_marklist *marklist; }; +enum delete_mode { + DELETE_CHAR, + DELETE_SOFTLINE, + DELETE_HARDLINE +}; + ledit_buffer *ledit_buffer_create(ledit_common *common, ledit_theme *theme, ledit_window *window); int ledit_buffer_load_file(ledit_buffer *buffer, char *filename, int line, char **errstr); int ledit_buffer_write_to_file(ledit_buffer *buffer, char *filename, char **errstr); @@ -110,7 +116,7 @@ void ledit_buffer_delete_line_entries_base(ledit_buffer *buffer, int index1, int void ledit_buffer_delete_line_entry_base(ledit_buffer *buffer, int index); int ledit_buffer_delete_unicode_char_base(ledit_buffer *buffer, int line_index, int byte_index, int dir); void ledit_buffer_delete_range_base( - ledit_buffer *buffer, int line_based, + ledit_buffer *buffer, enum delete_mode delmode, int line_index1, int byte_index1, int line_index2, int byte_index2, int *new_line_ret, int *new_byte_ret, @@ -135,7 +141,7 @@ void ledit_buffer_delete_line_entries(ledit_buffer *buffer, int index1, int inde void ledit_buffer_delete_line_entry(ledit_buffer *buffer, int index); int ledit_buffer_delete_unicode_char(ledit_buffer *buffer, int line_index, int byte_index, int dir); void ledit_buffer_delete_range( - ledit_buffer *buffer, int line_based, + ledit_buffer *buffer, enum delete_mode delmode, int line_index1, int byte_index1, int line_index2, int byte_index2, int *new_line_ret, int *new_byte_ret, diff --git a/keys_basic.c b/keys_basic.c @@ -78,6 +78,7 @@ static struct { } key_stack = {0, 0, NULL}; static struct action (*grab_char_cb)(ledit_buffer *buffer, char *text, int len) = NULL; +static int hard_line_based = 1; void basic_key_cleanup(void) { @@ -322,62 +323,72 @@ finalize_repetition_stack(void) { repetition_stack.tmp_stack = tmpstack; } -/* get the new line and softline when moving 'movement' softlines up or +/* get the new line and softline when moving 'movement' softlines + (or hardlines if hard_line_based is set) up or down (negative means up, positive means down) */ static void get_new_line_softline( ledit_buffer *buffer, int cur_line, int cur_index, int movement, int *new_line_ret, int *new_softline_ret) { - ledit_line *line = ledit_buffer_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_buffer_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; + if (hard_line_based) { + *new_line_ret = cur_line + movement; + if (*new_line_ret < 0) + *new_line_ret = 0; + else if (*new_line_ret >= buffer->lines_num) + *new_line_ret = buffer->lines_num - 1; + *new_softline_ret = 0; + } else { + ledit_line *line = ledit_buffer_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 { - *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_buffer_get_line(buffer, endline); - softlines = pango_layout_get_line_count(line->layout); - movement += softlines; + movement -= (softlines - softline - 1); + int endline = cur_line + 1; + while (movement > 0 && endline < buffer->lines_num) { + line = ledit_buffer_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; } - endline++; - if (movement >= 0) { - *new_softline_ret = movement; + } else if (movement < 0) { + int softlines = 0; + if (softline + movement >= 0) { + *new_line_ret = cur_line; + *new_softline_ret = softline + movement; } else { - *new_softline_ret = 0; + movement += softline; + int endline = cur_line - 1; + while (movement < 0 && endline >= 0) { + line = ledit_buffer_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; } - *new_line_ret = endline; + } else { + *new_line_ret = cur_line; + *new_softline_ret = softline; } - } else { - *new_line_ret = cur_line; - *new_softline_ret = softline; } } @@ -396,8 +407,15 @@ delete_range( ledit_range cur_range, del_range; cur_range.line1 = buffer->cur_line; cur_range.byte1 = buffer->cur_index; + enum delete_mode delmode = DELETE_CHAR; + if (line_based) { + if (hard_line_based) + delmode = DELETE_HARDLINE; + else + delmode = DELETE_SOFTLINE; + } ledit_buffer_delete_range( - buffer, line_based, + buffer, delmode, line_index1, byte_index1, line_index2, byte_index2, &buffer->cur_line, &buffer->cur_index, @@ -544,8 +562,10 @@ append_line_above(ledit_buffer *buffer, char *text, int len) { /* do this here already so the mode group is the same for the newline insertion */ enter_insert(buffer, text, len); ledit_line *ll = ledit_buffer_get_line(buffer, buffer->cur_line); + /* FIXME: this is more "elegant", but inefficient because this doesn't + actually need to be called when hard_line_based == 1 */ pango_layout_index_to_line_x(ll->layout, buffer->cur_index, 0, &sli, &x); - if (sli == 0) { + if (hard_line_based || sli == 0) { insert_text(buffer, buffer->cur_line, 0, "\n", -1, -1, -1, buffer->cur_line, 0, 1); } else { PangoLayoutLine *sl = pango_layout_get_line_readonly(ll->layout, sli); @@ -561,7 +581,7 @@ append_line_below(ledit_buffer *buffer, char *text, int len) { ledit_line *ll = ledit_buffer_get_line(buffer, buffer->cur_line); pango_layout_index_to_line_x(ll->layout, buffer->cur_index, 0, &sli, &x); PangoLayoutLine *sl = pango_layout_get_line_readonly(ll->layout, sli); - if (sl->start_index + sl->length == ll->len) { + if (hard_line_based || sl->start_index + sl->length == ll->len) { insert_text(buffer, buffer->cur_line, ll->len, "\n", -1, -1, -1, buffer->cur_line + 1, 0, 1); } else { insert_text(buffer, buffer->cur_line, sl->start_index + sl->length, "\n\n", -1, -1, -1, buffer->cur_line + 1, 0, 1); @@ -587,9 +607,13 @@ append_after_eol(ledit_buffer *buffer, char *text, int len) { /* make cursor jump back to original position on undo */ push_undo_empty_insert(buffer, buffer->cur_line, buffer->cur_index, 1); ledit_line *ll = ledit_buffer_get_line(buffer, buffer->cur_line); - pango_layout_index_to_line_x(ll->layout, buffer->cur_index, 0, &sli, &x); - PangoLayoutLine *sl = pango_layout_get_line_readonly(ll->layout, sli); - buffer->cur_index = sl->start_index + sl->length; + if (hard_line_based) { + buffer->cur_index = ll->len; + } else { + pango_layout_index_to_line_x(ll->layout, buffer->cur_index, 0, &sli, &x); + PangoLayoutLine *sl = pango_layout_get_line_readonly(ll->layout, sli); + buffer->cur_index = sl->start_index + sl->length; + } return (struct action){ACTION_NONE, NULL}; } @@ -629,6 +653,7 @@ move_to_line(ledit_buffer *buffer, char *text, int len) { return (struct action){ACTION_NONE, NULL}; } +/* FIXME: should these scrolling functions change behavior when hard_line_based == 1? */ static void scroll_lines(ledit_buffer *buffer, int lines, int dir) { int final_lines; @@ -802,20 +827,21 @@ static struct action delete_to_eol(ledit_buffer *buffer, char *text, int len) { (void)text; (void)len; - /* FIXME: move to separate function */ - if (!key_stack_empty()) { - clear_key_stack(); - ledit_window_show_message(buffer->window, "Invalid key", -1); - return (struct action){ACTION_NONE, NULL}; - } - int x, sli; + if (!key_stack_empty()) + return err_invalid_key(buffer); + int end, x, sli; ledit_line *ll = ledit_buffer_get_line(buffer, buffer->cur_line); - pango_layout_index_to_line_x(ll->layout, buffer->cur_index, 0, &sli, &x); - PangoLayoutLine *sl = pango_layout_get_line_readonly(ll->layout, sli); + if (hard_line_based) { + end = ll->len; + } else { + pango_layout_index_to_line_x(ll->layout, buffer->cur_index, 0, &sli, &x); + PangoLayoutLine *sl = pango_layout_get_line_readonly(ll->layout, sli); + end = sl->start_index + sl->length; + } delete_range( buffer, 0, 0, buffer->cur_line, buffer->cur_index, - buffer->cur_line, sl->start_index + sl->length, 1 + buffer->cur_line, end, 1 ); paste_buffer_line_based = 0; buffer->cur_index = ledit_buffer_get_legal_normal_pos( @@ -829,20 +855,22 @@ static struct action change_to_eol(ledit_buffer *buffer, char *text, int len) { (void)text; (void)len; - if (!key_stack_empty()) { - clear_key_stack(); - ledit_window_show_message(buffer->window, "Invalid key", -1); - return (struct action){ACTION_NONE, NULL}; - } + if (!key_stack_empty()) + return err_invalid_key(buffer); ledit_buffer_set_mode(buffer, INSERT); - int x, sli; + int end, x, sli; ledit_line *ll = ledit_buffer_get_line(buffer, buffer->cur_line); - pango_layout_index_to_line_x(ll->layout, buffer->cur_index, 0, &sli, &x); - PangoLayoutLine *sl = pango_layout_get_line_readonly(ll->layout, sli); + if (hard_line_based) { + end = ll->len; + } else { + pango_layout_index_to_line_x(ll->layout, buffer->cur_index, 0, &sli, &x); + PangoLayoutLine *sl = pango_layout_get_line_readonly(ll->layout, sli); + end = sl->start_index + sl->length; + } delete_range( buffer, 0, 0, buffer->cur_line, buffer->cur_index, - buffer->cur_line, sl->start_index + sl->length, 1 + buffer->cur_line, end, 1 ); paste_buffer_line_based = 0; ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line); @@ -896,7 +924,7 @@ change_cb(ledit_buffer *buffer, int line, int char_pos, enum key_type type) { /* this hackery is needed to avoid deleting the entire last line and instead leave an empty line - this should be made nicer (FIXME) */ int pos1 = buffer->cur_index, pos2 = char_pos, x, sli; - if (line_based) { + if (line_based && !hard_line_based) { ledit_line *ll = ledit_buffer_get_line(buffer, buffer->cur_line); pango_layout_index_to_line_x(ll->layout, buffer->cur_index, 0, &sli, &x); PangoLayoutLine *sl = pango_layout_get_line_readonly(ll->layout, sli); @@ -905,6 +933,10 @@ change_cb(ledit_buffer *buffer, int line, int char_pos, enum key_type type) { pango_layout_index_to_line_x(ll->layout, char_pos, 0, &sli, &x); sl = pango_layout_get_line_readonly(ll->layout, sli); pos2 = sl->start_index + sl->length; + } else if (line_based && hard_line_based) { + pos1 = 0; + ledit_line *ll = ledit_buffer_get_line(buffer, line); + pos2 = ll->len; } /* force line_based to 0 (see comment about hackery above) */ delete_range( @@ -989,6 +1021,9 @@ yank_lines(ledit_buffer *buffer, char *text, int len) { return (struct action){ACTION_NONE, NULL}; } +/* FIXME: delete_range and yank put different things in past_buffer - yank doesn't include + extra newlines at the beginning and end (this doesn't really matter because paste + ignores them anyways, but it is a bit weird) */ static void yank_cb(ledit_buffer *buffer, int line, int char_pos, enum key_type type) { int line_based = type == KEY_MOTION_LINE ? 1 : 0; @@ -999,7 +1034,7 @@ yank_cb(ledit_buffer *buffer, int line, int char_pos, enum key_type type) { swap(&l1, &l2); swap(&b1, &b2); } - if (line_based) { + if (line_based && !hard_line_based) { int x, sl1, sl2; ledit_line *ll1 = ledit_buffer_get_line(buffer, l1); pango_layout_index_to_line_x(ll1->layout, b1, 0, &sl1, &x); @@ -1011,6 +1046,11 @@ yank_cb(ledit_buffer *buffer, int line, int char_pos, enum key_type type) { ledit_buffer_copy_text_to_txtbuf( buffer, paste_buffer, l1, pl1->start_index, l2, pl2->start_index + pl2->length ); + } else if (line_based && hard_line_based) { + ledit_line *ll = ledit_buffer_get_line(buffer, l2); + ledit_buffer_copy_text_to_txtbuf( + buffer, paste_buffer, l1, 0, l2, ll->len + ); } else { ledit_buffer_copy_text_to_txtbuf( buffer, paste_buffer, l1, b1, l2, b2 @@ -1091,10 +1131,16 @@ paste_normal(ledit_buffer *buffer, char *text, int len) { int x, softline; ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line); ledit_line *ll = ledit_buffer_get_line(buffer, buffer->cur_line); - pango_layout_index_to_line_x(ll->layout, buffer->cur_index, 0, &softline, &x); - PangoLayoutLine *sl = pango_layout_get_line_readonly(ll->layout, softline); + int brk = 0; + if (hard_line_based) { + brk = ll->len; + } else { + pango_layout_index_to_line_x(ll->layout, buffer->cur_index, 0, &softline, &x); + PangoLayoutLine *sl = pango_layout_get_line_readonly(ll->layout, softline); + brk = sl->start_index + sl->length; + } insert_text( - buffer, buffer->cur_line, sl->start_index + sl->length, + buffer, buffer->cur_line, brk, "\n", -1, -1, -1, buffer->cur_line, buffer->cur_index, 1 ); int text_len = paste_buffer->len; @@ -1143,10 +1189,14 @@ paste_normal_backwards(ledit_buffer *buffer, char *text, int len) { int x, softline; ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line); ledit_line *ll = ledit_buffer_get_line(buffer, buffer->cur_line); - pango_layout_index_to_line_x(ll->layout, buffer->cur_index, 0, &softline, &x); - PangoLayoutLine *sl = pango_layout_get_line_readonly(ll->layout, softline); + int brk = 0; + if (!hard_line_based) { + pango_layout_index_to_line_x(ll->layout, buffer->cur_index, 0, &softline, &x); + PangoLayoutLine *sl = pango_layout_get_line_readonly(ll->layout, softline); + brk = sl->start_index; + } insert_text( - buffer, buffer->cur_line, sl->start_index, + buffer, buffer->cur_line, brk, "\n", -1, -1, -1, buffer->cur_line, buffer->cur_index, 1 ); int text_len = paste_buffer->len; @@ -1341,8 +1391,11 @@ move_to_eol(ledit_buffer *buffer, char *text, int len) { &new_line, &new_softline ); ledit_line *ll = ledit_buffer_get_line(buffer, new_line); - PangoLayoutLine *sl = pango_layout_get_line_readonly(ll->layout, new_softline); - int end_index = sl->start_index + sl->length; + int end_index = ll->len; + if (!hard_line_based) { + PangoLayoutLine *sl = pango_layout_get_line_readonly(ll->layout, new_softline); + end_index = sl->start_index + sl->length; + } if (cb != NULL) { cb(buffer, new_line, end_index, KEY_MOTION_CHAR); } else { @@ -1671,7 +1724,7 @@ join_lines(ledit_buffer *buffer, char *text, int len) { oldlen = ll1->len; /* FIXME: truncate whitespace to one space */ ledit_buffer_delete_range( - buffer, 0, + buffer, DELETE_CHAR, cur_line, ll1->len, cur_line + 1, 0, NULL, NULL, &del_range, buf ); @@ -1697,12 +1750,16 @@ insert_at_beginning(ledit_buffer *buffer, char *text, int len) { if (!key_stack_empty()) return err_invalid_key(buffer); enter_insert(buffer, text, len); - int x, sli; - ledit_line *ll = ledit_buffer_get_line(buffer, buffer->cur_line); - pango_layout_index_to_line_x(ll->layout, buffer->cur_index, 0, &sli, &x); - PangoLayoutLine *pl = pango_layout_get_line_readonly(ll->layout, sli); + int new_index = 0; + if (!hard_line_based) { + int x, sli; + ledit_line *ll = ledit_buffer_get_line(buffer, buffer->cur_line); + pango_layout_index_to_line_x(ll->layout, buffer->cur_index, 0, &sli, &x); + PangoLayoutLine *pl = pango_layout_get_line_readonly(ll->layout, sli); + new_index = pl->start_index; + } push_undo_empty_insert(buffer, buffer->cur_line, buffer->cur_index, 1); - buffer->cur_index = pl->start_index; + buffer->cur_index = new_index; ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line); return (struct action){ACTION_NONE, NULL}; } @@ -1711,20 +1768,25 @@ static struct action cursor_to_first_non_ws(ledit_buffer *buffer, char *text, int len) { (void)text; (void)len; - int x, sli; motion_callback cb; int num = get_key_repeat_and_motion_cb(&cb); if (num != 0) return err_invalid_key(buffer); + int new_index = 0; ledit_line *ll = ledit_buffer_get_line(buffer, buffer->cur_line); - pango_layout_index_to_line_x(ll->layout, buffer->cur_index, 0, &sli, &x); - PangoLayoutLine *pl = pango_layout_get_line_readonly(ll->layout, sli); - int new_index = ledit_line_next_non_whitespace(ll, pl->start_index); - /* next non-whitespace might be on next softline */ - if (new_index >= pl->start_index + pl->length) { - new_index = ledit_buffer_prev_cursor_pos( - buffer, buffer->cur_line, pl->start_index + pl->length, 1 - ); + if (hard_line_based) { + new_index = ledit_line_next_non_whitespace(ll, 0); + } else { + int x, sli; + pango_layout_index_to_line_x(ll->layout, buffer->cur_index, 0, &sli, &x); + PangoLayoutLine *pl = pango_layout_get_line_readonly(ll->layout, sli); + new_index = ledit_line_next_non_whitespace(ll, pl->start_index); + /* next non-whitespace might be on next softline */ + if (new_index >= pl->start_index + pl->length) { + new_index = ledit_buffer_prev_cursor_pos( + buffer, buffer->cur_line, pl->start_index + pl->length, 1 + ); + } } if (cb != NULL) { cb(buffer, buffer->cur_line, new_index, KEY_MOTION_CHAR); @@ -1748,13 +1810,17 @@ cursor_to_beginning(ledit_buffer *buffer, char *text, int len) { if (num != 0) return err_invalid_key(buffer); /* FIXME: should anything be done with num? */ - ledit_line *ll = ledit_buffer_get_line(buffer, buffer->cur_line); - pango_layout_index_to_line_x(ll->layout, buffer->cur_index, 0, &sli, &x); - PangoLayoutLine *pl = pango_layout_get_line_readonly(ll->layout, sli); + int start_index = 0; + if (!hard_line_based) { + ledit_line *ll = ledit_buffer_get_line(buffer, buffer->cur_line); + pango_layout_index_to_line_x(ll->layout, buffer->cur_index, 0, &sli, &x); + PangoLayoutLine *pl = pango_layout_get_line_readonly(ll->layout, sli); + start_index = pl->start_index; + } if (cb != NULL) { - cb(buffer, buffer->cur_line, pl->start_index, KEY_MOTION_CHAR); + cb(buffer, buffer->cur_line, start_index, KEY_MOTION_CHAR); } else { - buffer->cur_index = pl->start_index; + buffer->cur_index = start_index; if (buffer->common->mode == VISUAL) { ledit_buffer_set_selection( buffer, @@ -2166,6 +2232,31 @@ replace(ledit_buffer *buffer, char *text, int len) { return (struct action){ACTION_NONE, NULL}; } +static void +set_hard_line_based(ledit_buffer *buffer, int hl) { + hard_line_based = hl; + char *text = hl ? "|HL" : "|SL"; + ledit_window_set_mode_extra_text(buffer->window, text); +} + +static struct action +toggle_hard_line_based(ledit_buffer *buffer, char *text, int len) { + (void)buffer; + (void)text; + (void)len; + int num = get_key_repeat(); + if (num != 0) + return err_invalid_key(buffer); + set_hard_line_based(buffer, !hard_line_based); + return (struct action){ACTION_NONE, NULL}; +} + +/* FIXME: this is sort of all over the place and ugly */ +void +keys_basic_init(ledit_buffer *buffer) { + set_hard_line_based(buffer, 1); +} + static struct action handle_key(ledit_buffer *buffer, char *key_text, int len, KeySym sym, unsigned int key_state, int lang_index, int *found) { struct key *cur_keys = keys[lang_index].keys; diff --git a/keys_basic.h b/keys_basic.h @@ -1,2 +1,3 @@ +void keys_basic_init(ledit_buffer *buffer); void basic_key_cleanup(void); struct action basic_key_handler(ledit_buffer *buffer, XEvent *event, int lang_index); diff --git a/keys_basic_config.h b/keys_basic_config.h @@ -1,3 +1,4 @@ +/* FIXME: these aren't really used properly */ enum key_type { KEY_NONE = 0, KEY_MISC = 1, @@ -93,6 +94,7 @@ static struct action replace(ledit_buffer *buffer, char *text, int len); static struct action cursor_to_first_non_ws(ledit_buffer *buffer, char *text, int len); static struct action join_lines(ledit_buffer *buffer, char *text, int len); static struct action insert_at_beginning(ledit_buffer *buffer, char *text, int len); +static struct action toggle_hard_line_based(ledit_buffer *buffer, char *text, int len); /* FIXME: maybe sort these and use binary search -> but that would mess with the catch-all keys */ @@ -111,6 +113,7 @@ static struct key keys_en[] = { {"j", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_down}, {"k", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_up}, {"h", ControlMask, 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_left}, + {"t", ControlMask, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &toggle_hard_line_based}, {NULL, 0, XK_space, NORMAL|VISUAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_right}, {"j", ControlMask, 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_down}, {"n", ControlMask, 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_down}, diff --git a/ledit.c b/ledit.c @@ -292,6 +292,7 @@ setup(int argc, char *argv[]) { } ledit_buffer_set_mode(buffer, NORMAL); ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index); + keys_basic_init(buffer); redraw(); } diff --git a/memory.c b/memory.c @@ -47,3 +47,20 @@ ledit_realloc(void *ptr, size_t size) { fatal_err("Out of memory.\n"); return new_ptr; } + +/* Concatenate the two given strings and return the result. + This allocates new memory for the result string, unlike + the actual strcat. Aborts program on error */ +char * +ledit_strcat(const char *str1, const char *str2) { + int len1, len2; + char *ret; + + len1 = strlen(str1); + len2 = strlen(str2); + ret = ledit_malloc(len1 + len2 + 1); + strcpy(ret, str1); + strcpy(ret + len1, str2); + + return ret; +} diff --git a/memory.h b/memory.h @@ -3,3 +3,4 @@ char *ledit_strndup(const char *s, size_t n); void *ledit_malloc(size_t size); void *ledit_calloc(size_t nmemb, size_t size); void *ledit_realloc(void *ptr, size_t size); +char *ledit_strcat(const char *str1, const char *str2); diff --git a/window.c b/window.c @@ -240,20 +240,24 @@ ledit_window_hide_message(ledit_window *window) { void ledit_window_set_mode(ledit_window *window, enum ledit_mode mode) { + char *text; switch (mode) { case NORMAL: - pango_layout_set_text(window->bb->mode, "Normal", -1); + text = "Normal"; break; case VISUAL: - pango_layout_set_text(window->bb->mode, "Visual", -1); + text = "Visual"; break; case INSERT: - pango_layout_set_text(window->bb->mode, "Insert", -1); + text = "Insert"; break; default: - pango_layout_set_text(window->bb->mode, "ledit is buggy", -1); + text = "ledit is buggy"; break; } + char *final_text = ledit_strcat(text, window->mode_extra_text ? window->mode_extra_text : ""); + pango_layout_set_text(window->bb->mode, final_text, -1); + free(final_text); pango_layout_get_pixel_size(window->bb->mode, &window->bb->mode_w, &window->bb->mode_h); ledit_draw_grow(window, window->bb->mode_draw, window->bb->mode_w, window->bb->mode_h); XftDrawRect(window->bb->mode_draw->xftdraw, &window->theme->text_bg, 0, 0, window->bb->mode_w, window->bb->mode_h); @@ -261,6 +265,12 @@ ledit_window_set_mode(ledit_window *window, enum ledit_mode mode) { recalc_text_size(window); } +void +ledit_window_set_mode_extra_text(ledit_window *window, char *text) { + window->mode_extra_text = ledit_strdup(text); + ledit_window_set_mode(window, window->common->mode); +} + /* FIXME: give these functions more sensible names */ static void get_scroll_pos_height(ledit_window *window, double *pos, double *height) { @@ -411,6 +421,7 @@ ledit_window_create(ledit_common *common, ledit_theme *theme) { window->scroll_grab_handle = 0; window->w = 500; window->h = 500; + window->mode_extra_text = NULL; memset(&window->wattrs, 0, sizeof(attrs)); window->wattrs.background_pixel = BlackPixel(common->dpy, common->screen); @@ -516,6 +527,8 @@ ledit_window_destroy(ledit_window *window) { g_object_unref(window->bb->line); ledit_draw_destroy(window, window->bb->mode_draw); ledit_draw_destroy(window, window->bb->line_draw); + if (window->mode_extra_text) + free(window->mode_extra_text); free(window->bb->line_text); free(window->bb); free(window); diff --git a/window.h b/window.h @@ -38,6 +38,7 @@ typedef struct { void *paste_cb_data; void *scroll_cb_data; void *button_cb_data; + char *mode_extra_text; } ledit_window; ledit_window *ledit_window_create(ledit_common *common, ledit_theme *theme); @@ -62,6 +63,7 @@ void ledit_window_show_message(ledit_window *window, char *text, int len); void ledit_window_hide_message(ledit_window *window); int ledit_window_message_shown(ledit_window *window); void ledit_window_set_mode(ledit_window *window, enum ledit_mode mode); +void ledit_window_set_mode_extra_text(ledit_window *window, char *text); void ledit_window_set_scroll_max(ledit_window *window, long max); void ledit_window_set_scroll_pos(ledit_window *window, long pos);