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