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 123a3087ad0bc4f772f0cd0a17caf95304092f87
parent 82723082181a863b7bc7c7cbbcc92da5c30caf6d
Author: lumidify <nobody@lumidify.org>
Date:   Wed,  8 Dec 2021 20:56:21 +0100

Start implementing substitution

Diffstat:
Mbuffer.c | 16++++++++++++++++
Mbuffer.h | 10++++++++++
Mkeys.c | 6+++++-
Mkeys_command.c | 121++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msearch.c | 2+-
Mview.c | 14++++++++++++++
Mview.h | 15+++++++++++++++
7 files changed, 181 insertions(+), 3 deletions(-)

diff --git a/buffer.c b/buffer.c @@ -143,6 +143,22 @@ buffer_create(ledit_common *common) { return buffer; } +void +buffer_lock_all_views_except(ledit_buffer *buffer, ledit_view *view, char *lock_text) { + for (size_t i = 0; i < buffer->views_num; i++) { + if (buffer->views[i] != view) { + view_lock(buffer->views[i], lock_text); + } + } +} + +void +buffer_unlock_all_views(ledit_buffer *buffer) { + for (size_t i = 0; i < buffer->views_num; i++) { + view_unlock(buffer->views[i]); + } +} + static void set_view_hard_line_text(ledit_buffer *buffer, ledit_view *view) { char *text = buffer->hard_line_based ? "|HL" : "|SL"; diff --git a/buffer.h b/buffer.h @@ -49,6 +49,16 @@ struct ledit_buffer { ledit_buffer *buffer_create(ledit_common *common); /* + * Lock all views except the given view. + */ +void buffer_lock_all_views_except(ledit_buffer *buffer, ledit_view *view, char *lock_text); + +/* + * Unlock all views. + */ +void buffer_unlock_all_views(ledit_buffer *buffer); + +/* * Set the hard line mode of the buffer and update the * displayed mode in all views. */ diff --git a/keys.c b/keys.c @@ -27,7 +27,11 @@ get_language_index(char *lang) { } /* FIXME: Does this break anything? */ -static unsigned int importantmod = ShiftMask | ControlMask | Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask; +/*static unsigned int importantmod = ShiftMask | ControlMask | Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask;*/ +/* FIXME: ShiftMask is currently masked away anyways, so it isn't really important */ +/* FIXME: The Mod*Masks can be remapped, so it isn't really clear what is what */ +/* most are disabled now to avoid issues with e.g. numlock */ +static unsigned int importantmod = ShiftMask | ControlMask | Mod1Mask; #define XK_ANY_MOD UINT_MAX int diff --git a/keys_command.c b/keys_command.c @@ -27,6 +27,18 @@ #include "keys_command.h" #include "keys_command_config.h" +static char *last_search = NULL; +static char *last_replacement = NULL; +static int last_replacement_global = 0; + +static int +view_locked_error(ledit_view *view) { + window_show_message(view->window, view->lock_text, -1); + return 0; +} + +#define CHECK_VIEW_LOCKED if (view->lock_text) return view_locked_error(view) + /* FIXME: history for search and commands */ static int create_view(ledit_view *view, char *cmd, size_t l1, size_t l2); @@ -104,7 +116,107 @@ handle_substitute(ledit_view *view, char *cmd, size_t l1, size_t l2) { (void)cmd; (void)l1; (void)l2; - printf("substitute\n"); + CHECK_VIEW_LOCKED; + cmd++; /* remove 's' at beginning */ + size_t len = strlen(cmd); + if (len == 0) goto error; + /* FIXME: utf8 */ + char sep = cmd[0]; + cmd++; + char *next = strchr(cmd, sep); + if (next == NULL) goto error; + *next = '\0'; + next++; + char *last = strchr(next, sep); + if (last == NULL) goto error; + *last = '\0'; + last++; + int confirm = 0, global = 0; + char *c = last; + while (*c != '\0') { + switch (*c) { + case 'c': + confirm = 1; + break; + case 'g': + global = 1; + break; + default: + goto error; + } + c++; + } + free(last_search); + free(last_replacement); + last_search = ledit_strdup(cmd); + last_replacement = ledit_strdup(next); + last_replacement_global = global; + + if (confirm) { + buffer_lock_all_views_except(view->buffer, view, "Ongoing substitution in other view."); + buffer_unlock_all_views(view->buffer); + } else { + int num = 0; + int start_undo_group = 1; + size_t slen = strlen(last_search); + size_t rlen = strlen(last_replacement); + txtbuf *buf = txtbuf_new(); /* FIXME: don't allocate new every time */ + view_wipe_line_cursor_attrs(view, view->cur_line); + for (size_t i = l1 - 1; i < l2; i++) { + ledit_line *ll = buffer_get_line(view->buffer, i); + buffer_normalize_line(ll); + char *pos = strstr(ll->text, last_search); + while (pos != NULL) { + size_t index = (size_t)(pos - ll->text); + ledit_range cur_range, del_range; + cur_range.line1 = view->cur_line; + cur_range.byte1 = view->cur_line; + view_delete_range( + view, DELETE_CHAR, + i, index, + i, index + slen, + &view->cur_line, &view->cur_index, + &del_range, buf + ); + cur_range.line2 = view->cur_line; + cur_range.byte2 = view->cur_index; + undo_push_delete( + view->buffer->undo, buf, del_range, cur_range, start_undo_group, view->mode + ); + start_undo_group = 0; + txtbuf ins_buf = {.text = last_replacement, .len = rlen, .cap = rlen}; + cur_range.line1 = view->cur_line; + cur_range.byte1 = view->cur_index; + del_range.line1 = i; + del_range.byte1 = index; + size_t cur_line, cur_index; + buffer_insert_text_with_newlines( + view->buffer, i, index, last_replacement, rlen, + &cur_line, &cur_index + ); + cur_range.line2 = view->cur_line; + cur_range.byte2 = view->cur_index; + del_range.line2 = cur_line; + del_range.byte2 = cur_index; + undo_push_insert( + view->buffer->undo, &ins_buf, del_range, cur_range, 0, view->mode + ); + num++; + if (!global) break; + buffer_normalize_line(ll); /* just in case */ + pos = strstr(ll->text + index + rlen, last_search); + } + } + /* FIXME: show number replaced */ + /* this doesn't need to be added to the undo stack since it's called on undo/redo anyways */ + view->cur_index = view_get_legal_normal_pos(view, view->cur_line, view->cur_index); + view_set_line_cursor_attrs(view, view->cur_line, view->cur_index); + view_ensure_cursor_shown(view); + txtbuf_destroy(buf); + } + return 0; +error: + window_show_message(view->window, "Invalid command", -1); return 0; } @@ -133,6 +245,7 @@ $ last line */ /* FIXME: ACTUALLY USE LEN!!! */ +/* FIXME: allow using marks and selection range here */ static int parse_range(ledit_view *view, char *cmd, size_t len, char **cmd_ret, size_t *line1_ret, size_t *line2_ret, int *l1_valid, int *l2_valid) { (void)len; @@ -180,6 +293,7 @@ parse_range(ledit_view *view, char *cmd, size_t len, char **cmd_ret, size_t *lin l1 = 1; l2 = view->lines_num; *l1_valid = *l2_valid = 1; + c++; break; } else { return 1; @@ -204,6 +318,8 @@ parse_range(ledit_view *view, char *cmd, size_t len, char **cmd_ret, size_t *lin } if ((!*l1_valid || !*l2_valid) && !(s & START_RANGE)) return 1; + if ((*l1_valid || *l2_valid) && (l1 == 0 || l2 == 0 || l1 > view->lines_num || l2 > view->lines_num)) + return 1; /* FIXME: better error messages */ *cmd_ret = c; *line1_ret = l1; *line2_ret = l2; @@ -221,6 +337,7 @@ handle_cmd(ledit_view *view, char *cmd, size_t len) { if (parse_range(view, cmd, len, &c, &l1, &l2, &l1_valid, &l2_valid)) return 0; int range_given = l1_valid && l2_valid; + /* FIXME: mandatory range */ for (size_t i = 0; i < LENGTH(cmds); i++) { if (!strncmp(cmds[i].cmd, c, strlen(cmds[i].cmd)) && (!range_given || cmds[i].type == CMD_OPTIONAL_RANGE)) { @@ -335,6 +452,7 @@ search_next(ledit_view *view) { view_wipe_line_cursor_attrs(view, view->cur_line); enum ledit_search_state ret = ledit_search_next(view, &view->cur_line, &view->cur_index); view_set_line_cursor_attrs(view, view->cur_line, view->cur_index); + view_ensure_cursor_shown(view); if (ret != SEARCH_NORMAL) window_show_message(view->window, search_state_to_str(ret), -1); } @@ -344,6 +462,7 @@ search_prev(ledit_view *view) { view_wipe_line_cursor_attrs(view, view->cur_line); enum ledit_search_state ret = ledit_search_prev(view, &view->cur_line, &view->cur_index); view_set_line_cursor_attrs(view, view->cur_line, view->cur_index); + view_ensure_cursor_shown(view); if (ret != SEARCH_NORMAL) window_show_message(view->window, search_state_to_str(ret), -1); } diff --git a/search.c b/search.c @@ -19,7 +19,7 @@ #include "search.h" /* FIXME: make sure only whole utf8 chars are matched */ -char *last_search = NULL; +static char *last_search = NULL; enum { FORWARD, BACKWARD diff --git a/view.c b/view.c @@ -112,6 +112,7 @@ view_create(ledit_buffer *buffer, ledit_theme *theme, enum ledit_mode mode, size view->window = window_create(buffer->common, theme, mode); view->theme = theme; view->cache = cache_create(buffer->common->dpy); + view->lock_text = NULL; view->cur_action = (struct action){ACTION_NONE, NULL}; window_set_scroll_callback(view->window, &view_scroll_handler, view); window_set_button_callback(view->window, &view_button_handler, view); @@ -147,6 +148,18 @@ view_create(ledit_buffer *buffer, ledit_theme *theme, enum ledit_mode mode, size return view; } +void +view_lock(ledit_view *view, char *lock_text) { + free(view->lock_text); + view->lock_text = ledit_strdup(lock_text); +} + +void +view_unlock(ledit_view *view) { + free(view->lock_text); + view->lock_text = NULL; +} + ledit_view_line * view_get_line(ledit_view *view, size_t index) { assert(index < view->lines_num); @@ -262,6 +275,7 @@ void view_destroy(ledit_view *view) { cache_destroy(view->cache); window_destroy(view->window); + free(view->lock_text); free(view->lines); free(view); } diff --git a/view.h b/view.h @@ -57,6 +57,7 @@ struct ledit_view { ledit_theme *theme; /* current theme in use */ ledit_cache *cache; /* cache for pixmaps and pango layouts */ ledit_view_line *lines; /* array of lines, stored as gap buffer */ + char *lock_text; /* text to show if view is locked, i.e. no edits allowed */ /* current command type - used by key handler in keys_command.c */ enum ledit_command_type cur_command_type; struct action cur_action; /* current action to execute on key press */ @@ -98,6 +99,20 @@ ledit_view *view_create( ); /* + * Lock a view. + * Views are locked for instance when substitution with confirmation is + * being performed in another view to avoid an inconsistent state. + * This currently only sets the lock text - commands using the view need + * to make sure to check that it isn't locked. + */ +void view_lock(ledit_view *view, char *text); + +/* + * Unlock a view. + */ +void view_unlock(ledit_view *view); + +/* * Get the view line at the given index. */ ledit_view_line *view_get_line(ledit_view *view, size_t index);