tStart implementing substitution - 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 <[email protected]> | |
Date: Wed, 8 Dec 2021 20:56:21 +0100 | |
Start implementing substitution | |
Diffstat: | |
M buffer.c | 16 ++++++++++++++++ | |
M buffer.h | 10 ++++++++++ | |
M keys.c | 6 +++++- | |
M keys_command.c | 121 +++++++++++++++++++++++++++++… | |
M search.c | 2 +- | |
M view.c | 14 ++++++++++++++ | |
M view.h | 15 +++++++++++++++ | |
7 files changed, 181 insertions(+), 3 deletions(-) | |
--- | |
diff --git a/buffer.c b/buffer.c | |
t@@ -143,6 +143,22 @@ buffer_create(ledit_common *common) { | |
return buffer; | |
} | |
+void | |
+buffer_lock_all_views_except(ledit_buffer *buffer, ledit_view *view, char *loc… | |
+ 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 | |
t@@ -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… | |
+ | |
+/* | |
+ * 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 | |
t@@ -27,7 +27,11 @@ get_language_index(char *lang) { | |
} | |
/* FIXME: Does this break anything? */ | |
-static unsigned int importantmod = ShiftMask | ControlMask | Mod1Mask | Mod2Ma… | |
+/*static unsigned int importantmod = ShiftMask | ControlMask | Mod1Mask | Mod2… | |
+/* FIXME: ShiftMask is currently masked away anyways, so it isn't really impor… | |
+/* 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 | |
t@@ -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); | |
t@@ -104,7 +116,107 @@ handle_substitute(ledit_view *view, char *cmd, size_t l1… | |
(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 subs… | |
+ 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… | |
+ 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_ra… | |
+ ); | |
+ start_undo_group = 0; | |
+ txtbuf ins_buf = {.text = last_replacement, .l… | |
+ 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, … | |
+ &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, c… | |
+ ); | |
+ num++; | |
+ if (!global) break; | |
+ buffer_normalize_line(ll); /* just in case */ | |
+ pos = strstr(ll->text + index + rlen, last_sea… | |
+ } | |
+ } | |
+ /* FIXME: show number replaced */ | |
+ /* this doesn't need to be added to the undo stack since it's … | |
+ view->cur_index = view_get_legal_normal_pos(view, view->cur_li… | |
+ view_set_line_cursor_attrs(view, view->cur_line, view->cur_ind… | |
+ view_ensure_cursor_shown(view); | |
+ txtbuf_destroy(buf); | |
+ } | |
+ return 0; | |
+error: | |
+ window_show_message(view->window, "Invalid command", -1); | |
return 0; | |
} | |
t@@ -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 *l… | |
(void)len; | |
t@@ -180,6 +293,7 @@ parse_range(ledit_view *view, char *cmd, size_t len, char … | |
l1 = 1; | |
l2 = view->lines_num; | |
*l1_valid = *l2_valid = 1; | |
+ c++; | |
break; | |
} else { | |
return 1; | |
t@@ -204,6 +318,8 @@ parse_range(ledit_view *view, char *cmd, size_t len, char … | |
} | |
if ((!*l1_valid || !*l2_valid) && !(s & START_RANGE)) | |
return 1; | |
+ if ((*l1_valid || *l2_valid) && (l1 == 0 || l2 == 0 || l1 > view->line… | |
+ return 1; /* FIXME: better error messages */ | |
*cmd_ret = c; | |
*line1_ret = l1; | |
*line2_ret = l2; | |
t@@ -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)) { | |
t@@ -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_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… | |
} | |
t@@ -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_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 | |
t@@ -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 | |
t@@ -112,6 +112,7 @@ view_create(ledit_buffer *buffer, ledit_theme *theme, enum… | |
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); | |
t@@ -147,6 +148,18 @@ view_create(ledit_buffer *buffer, ledit_theme *theme, enu… | |
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); | |
t@@ -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 | |
t@@ -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 e… | |
/* 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 */ | |
t@@ -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); |