summaryrefslogtreecommitdiff
path: root/examples/dte/replace.c
diff options
context:
space:
mode:
authorMitja Felicijan <mitja.felicijan@gmail.com>2023-11-09 23:19:53 +0100
committerMitja Felicijan <mitja.felicijan@gmail.com>2023-11-09 23:19:53 +0100
commit1566b6faa8534118c3566188181367cd0868468f (patch)
tree1de8d4b369efb5e592685a31088f798a6b63ffa1 /examples/dte/replace.c
parent349991bf6efe473ab9a5cbdae0a8114d72b997e3 (diff)
downloadcrep-1566b6faa8534118c3566188181367cd0868468f.tar.gz
Added partial matching and introduced threads
Diffstat (limited to 'examples/dte/replace.c')
-rw-r--r--examples/dte/replace.c256
1 files changed, 256 insertions, 0 deletions
diff --git a/examples/dte/replace.c b/examples/dte/replace.c
new file mode 100644
index 0000000..028d474
--- /dev/null
+++ b/examples/dte/replace.c
@@ -0,0 +1,256 @@
+#include <stdlib.h>
+#include "replace.h"
+#include "buffer.h"
+#include "change.h"
+#include "editor.h"
+#include "error.h"
+#include "regexp.h"
+#include "screen.h"
+#include "selection.h"
+#include "util/debug.h"
+#include "util/string.h"
+#include "util/xmalloc.h"
+#include "view.h"
+#include "window.h"
+
+static void build_replacement (
+ String *buf,
+ const char *line,
+ const char *format,
+ const regmatch_t *matches
+) {
+ for (size_t i = 0; format[i]; ) {
+ char ch = format[i++];
+ size_t match_idx;
+ if (ch == '\\') {
+ if (unlikely(format[i] == '\0')) {
+ break;
+ }
+ ch = format[i++];
+ if (ch < '1' || ch > '9') {
+ string_append_byte(buf, ch);
+ continue;
+ }
+ match_idx = ch - '0';
+ } else if (ch == '&') {
+ match_idx = 0;
+ } else {
+ string_append_byte(buf, ch);
+ continue;
+ }
+ const regmatch_t *match = &matches[match_idx];
+ regoff_t len = match->rm_eo - match->rm_so;
+ if (len > 0) {
+ string_append_buf(buf, line + match->rm_so, (size_t)len);
+ }
+ }
+}
+
+/*
+ * s/abc/x
+ *
+ * string to match against
+ * -------------------------------------------
+ * "foo abc bar abc baz" "foo abc bar abc baz"
+ * "foo x bar abc baz" " bar abc baz"
+ */
+static unsigned int replace_on_line (
+ View *view,
+ StringView *line,
+ regex_t *re,
+ const char *format,
+ BlockIter *bi,
+ ReplaceFlags *flagsp
+) {
+ const unsigned char *buf = line->data;
+ unsigned char *alloc = NULL;
+ EditorState *e = view->window->editor;
+ ReplaceFlags flags = *flagsp;
+ regmatch_t matches[32];
+ size_t pos = 0;
+ int eflags = 0;
+ unsigned int nr = 0;
+
+ while (regexp_exec (
+ re,
+ buf + pos,
+ line->length - pos,
+ ARRAYLEN(matches),
+ matches,
+ eflags
+ )) {
+ regoff_t match_len = matches[0].rm_eo - matches[0].rm_so;
+ bool skip = false;
+
+ // Move cursor to beginning of the text to replace
+ block_iter_skip_bytes(bi, matches[0].rm_so);
+ view->cursor = *bi;
+
+ if (flags & REPLACE_CONFIRM) {
+ switch (status_prompt(e, "Replace? [Y/n/a/q]", "ynaq")) {
+ case 'y':
+ break;
+ case 'n':
+ skip = true;
+ break;
+ case 'a':
+ flags &= ~REPLACE_CONFIRM;
+ *flagsp = flags;
+
+ // Record rest of the changes as one chain
+ begin_change_chain();
+ break;
+ case 'q':
+ case 0:
+ *flagsp = flags | REPLACE_CANCEL;
+ goto out;
+ }
+ }
+
+ if (skip) {
+ // Move cursor after the matched text
+ block_iter_skip_bytes(&view->cursor, match_len);
+ } else {
+ String b = STRING_INIT;
+ build_replacement(&b, buf + pos, format, matches);
+
+ // line ref is invalidated by modification
+ if (buf == line->data && line->length != 0) {
+ BUG_ON(alloc);
+ alloc = xmemdup(buf, line->length);
+ buf = alloc;
+ }
+
+ buffer_replace_bytes(view, match_len, b.buffer, b.len);
+ nr++;
+
+ // Update selection length
+ if (view->selection) {
+ view->sel_eo += b.len;
+ view->sel_eo -= match_len;
+ }
+
+ // Move cursor after the replaced text
+ block_iter_skip_bytes(&view->cursor, b.len);
+ string_free(&b);
+ }
+ *bi = view->cursor;
+
+ if (!match_len) {
+ break;
+ }
+
+ if (!(flags & REPLACE_GLOBAL)) {
+ break;
+ }
+
+ pos += matches[0].rm_so + match_len;
+
+ // Don't match beginning of line again
+ eflags = REG_NOTBOL;
+ }
+
+out:
+ free(alloc);
+ return nr;
+}
+
+bool reg_replace(View *view, const char *pattern, const char *format, ReplaceFlags flags)
+{
+ if (unlikely(pattern[0] == '\0')) {
+ return error_msg("Search pattern must contain at least 1 character");
+ }
+
+ int re_flags = REG_NEWLINE;
+ re_flags |= (flags & REPLACE_IGNORE_CASE) ? REG_ICASE : 0;
+ re_flags |= (flags & REPLACE_BASIC) ? 0 : DEFAULT_REGEX_FLAGS;
+
+ regex_t re;
+ if (unlikely(!regexp_compile_internal(&re, pattern, re_flags))) {
+ return false;
+ }
+
+ BlockIter bi = block_iter(view->buffer);
+ size_t nr_bytes;
+ bool swapped = false;
+ if (view->selection) {
+ SelectionInfo info;
+ init_selection(view, &info);
+ view->cursor = info.si;
+ view->sel_so = info.so;
+ view->sel_eo = info.eo;
+ swapped = info.swapped;
+ bi = view->cursor;
+ nr_bytes = info.eo - info.so;
+ } else {
+ BlockIter eof = bi;
+ block_iter_eof(&eof);
+ nr_bytes = block_iter_get_offset(&eof);
+ }
+
+ // Record multiple changes as one chain only when replacing all
+ if (!(flags & REPLACE_CONFIRM)) {
+ begin_change_chain();
+ }
+
+ unsigned int nr_substitutions = 0;
+ size_t nr_lines = 0;
+ while (1) {
+ StringView line;
+ fill_line_ref(&bi, &line);
+
+ // Number of bytes to process
+ size_t count = line.length;
+ if (line.length > nr_bytes) {
+ // End of selection is not full line
+ line.length = nr_bytes;
+ }
+
+ unsigned int nr = replace_on_line(view, &line, &re, format, &bi, &flags);
+ if (nr) {
+ nr_substitutions += nr;
+ nr_lines++;
+ }
+
+ if (flags & REPLACE_CANCEL || count + 1 >= nr_bytes) {
+ break;
+ }
+
+ nr_bytes -= count + 1;
+ block_iter_next_line(&bi);
+ }
+
+ if (!(flags & REPLACE_CONFIRM)) {
+ end_change_chain(view);
+ }
+
+ regfree(&re);
+
+ if (nr_substitutions) {
+ info_msg (
+ "%u substitution%s on %zu line%s",
+ nr_substitutions,
+ (nr_substitutions > 1) ? "s" : "",
+ nr_lines,
+ (nr_lines > 1) ? "s" : ""
+ );
+ } else if (!(flags & REPLACE_CANCEL)) {
+ error_msg("Pattern '%s' not found", pattern);
+ }
+
+ if (view->selection) {
+ // Undo what init_selection() did
+ if (view->sel_eo) {
+ view->sel_eo--;
+ }
+ if (swapped) {
+ ssize_t tmp = view->sel_so;
+ view->sel_so = view->sel_eo;
+ view->sel_eo = tmp;
+ }
+ block_iter_goto_offset(&view->cursor, view->sel_eo);
+ view->sel_eo = SEL_EO_RECALC;
+ }
+
+ return (nr_substitutions > 0);
+}