diff options
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | doc/sciteco.7.template | 5 | ||||
| -rw-r--r-- | freebsd/Makefile | 5 | ||||
| -rw-r--r-- | src/Makefile.am | 8 | ||||
| -rw-r--r-- | src/cmdline.c | 9 | ||||
| -rw-r--r-- | src/core-commands.c | 73 | ||||
| -rw-r--r-- | src/error.h | 1 | ||||
| -rw-r--r-- | src/file-utils.c | 9 | ||||
| -rw-r--r-- | src/glob.c | 17 | ||||
| -rw-r--r-- | src/help.c | 5 | ||||
| -rw-r--r-- | src/interface-curses/interface.c | 65 | ||||
| -rw-r--r-- | src/interface-gtk/interface.c | 6 | ||||
| -rw-r--r-- | src/interface.h | 6 | ||||
| -rw-r--r-- | src/lsp.c | 15 | ||||
| -rw-r--r-- | src/main.c | 5 | ||||
| -rw-r--r-- | src/memory.c | 12 | ||||
| -rw-r--r-- | src/move-commands.c | 21 | ||||
| -rw-r--r-- | src/parser.c | 14 | ||||
| -rw-r--r-- | src/qreg-commands.c | 15 | ||||
| -rw-r--r-- | src/qreg.c | 19 | ||||
| -rw-r--r-- | src/qreg.h | 33 | ||||
| -rw-r--r-- | src/ring.c | 11 | ||||
| -rw-r--r-- | src/ring.h | 2 | ||||
| -rw-r--r-- | src/search.c | 82 | ||||
| -rw-r--r-- | src/spawn.c | 33 | ||||
| -rw-r--r-- | src/spawn.h | 13 | ||||
| -rw-r--r-- | src/view.c | 58 | ||||
| -rw-r--r-- | src/view.h | 10 | ||||
| -rw-r--r-- | tests/testsuite.at | 5 |
29 files changed, 292 insertions, 266 deletions
@@ -53,3 +53,4 @@ testsuite.dir # Created e.g. by `bear -- gmake` for clangd. # You may also have to create a symbolic link, so clangd finds it. compile_commands.json +compile_flags.txt diff --git a/doc/sciteco.7.template b/doc/sciteco.7.template index 14aee77..86ca68f 100644 --- a/doc/sciteco.7.template +++ b/doc/sciteco.7.template @@ -1878,6 +1878,11 @@ Analogous to \fB^V\fP, but upper-cases characters. Since \fB^W\fP is an immediate editing command, this can practically be typed only with upcarets in interactive mode. .TP +.SCITECO_TOPIC ^E^J ^E^M^J ^E^M +.BI ^E eol +\fB^E\fP followed by an end of line character (LF, CRLF or CR) is ignored, +so you can split a long string argument over several lines. +.TP .SCITECO_TOPIC ^E\[rs] ^E\[rs]q .BI ^E\(rs q Expands to the formatted number stored in the diff --git a/freebsd/Makefile b/freebsd/Makefile index c1769e0..16fdddd 100644 --- a/freebsd/Makefile +++ b/freebsd/Makefile @@ -17,17 +17,12 @@ curses_PKGNAMESUFFIX= -curses gtk_PKGNAMESUFFIX= -gtk USES= compiler:c11 gmake gnome groff pkgconfig -# Only required because we patch configure.ac: -USES+= autoreconf USE_GNOME= glib20 GNU_CONFIGURE= yes CONFIGURE_ARGS= CONFIGURE_OUTSOURCE= yes -EXTRA_PATCHES= ${FILESDIR}/opener.check-recovery.patch:-p1 \ - ${FILESDIR}/sbrk.patch:-p1 - MAKEFILE= GNUmakefile TEST_TARGET= check diff --git a/src/Makefile.am b/src/Makefile.am index fa6c0e4..eb532e3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -12,6 +12,7 @@ include $(top_srcdir)/bootstrap.am include $(top_srcdir)/contrib/scintilla.am # FIXME: Common flags should be in configure.ac +# See also compile_flags.txt. AM_CFLAGS = -std=gnu11 -Wall -Wno-initializer-overrides -Wno-unused-value AM_CPPFLAGS += -I$(top_srcdir)/contrib/rb3ptr -I$(top_srcdir)/contrib/terex \ -I$(top_srcdir)/contrib/sj.h @@ -22,7 +23,12 @@ if STATIC_EXECUTABLES AM_LDFLAGS += -all-static endif -BUILT_SOURCES = +BUILT_SOURCES = compile_flags.txt + +# for clangd +compile_flags.txt : GNUmakefile + printf '%s\n' $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) >$@ dist_noinst_SCRIPTS = symbols-extract.tes diff --git a/src/cmdline.c b/src/cmdline.c index 572b159..99bc802 100644 --- a/src/cmdline.c +++ b/src/cmdline.c @@ -173,11 +173,9 @@ teco_cmdline_insert(const gchar *data, gsize len, GError **error) */ g_clear_error(&tmp_error); - teco_qreg_t *cmdline_reg = teco_qreg_table_find(&teco_qreg_table_globals, "\e", 1); g_auto(teco_string_t) new_cmdline = {NULL, 0}; - - if (!cmdline_reg->vtable->get_string(cmdline_reg, &new_cmdline.data, &new_cmdline.len, - NULL, &tmp_error)) { + if (!teco_qreg_table_get_string(&teco_qreg_table_globals, "\e", + &new_cmdline.data, &new_cmdline.len, &tmp_error)) { teco_error_add_frame_toplevel(); teco_error_display_short(tmp_error); g_propagate_error(error, g_steal_pointer(&tmp_error)); @@ -384,9 +382,6 @@ teco_cmdline_keypress(const gchar *data, gsize len, GError **error) } teco_undo_clear(); - /* also empties all Scintilla undo buffers */ - teco_ring_set_scintilla_undo(TRUE); - teco_view_set_scintilla_undo(teco_qreg_view, TRUE); /* * FIXME: Reset main machine? */ diff --git a/src/core-commands.c b/src/core-commands.c index db3ae9d..6362d95 100644 --- a/src/core-commands.c +++ b/src/core-commands.c @@ -291,13 +291,11 @@ teco_state_start_backslash(teco_machine_main_t *ctx, GError **error) teco_current_doc_set_dot(teco_ranges[0].to); - teco_interface_ssm(SCI_BEGINUNDOACTION, 0, 0); + if (teco_current_doc_must_undo()) + undo__teco_interface_ssm(SCI_DELETERANGE, + teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0), len); teco_interface_ssm(SCI_ADDTEXT, len, (sptr_t)str); - teco_interface_ssm(SCI_ENDUNDOACTION, 0, 0); teco_ring_dirtify(); - - if (teco_current_doc_must_undo()) - undo__teco_interface_ssm(SCI_UNDO, 0, 0); } else { teco_qreg_t *qreg = ctx->qreg_table_locals->radix; assert(qreg != NULL); @@ -461,10 +459,8 @@ teco_state_start_break(teco_machine_main_t *ctx, GError **error) return; } - teco_qreg_t *reg = teco_qreg_table_find(&teco_qreg_table_globals, "_", 1); - g_assert(reg != NULL); teco_int_t v; - if (!reg->vtable->get_integer(reg, &v, error)) + if (!teco_qreg_table_get_integer(&teco_qreg_table_globals, "_", &v, error)) return; teco_bool_t rc; @@ -568,16 +564,11 @@ teco_state_start_cmdline_push(teco_machine_main_t *ctx, GError **error) const gchar *macro = (const gchar *)teco_cmdline_ssm(SCI_GETCHARACTERPOINTER, 0, 0); - teco_interface_ssm(SCI_BEGINUNDOACTION, 0, 0); + /* must always support undo on global register */ + teco_undo_interface_insert(0, teco_interface_ssm(SCI_GETLENGTH, 0, 0)); teco_interface_ssm(SCI_CLEARALL, 0, 0); + undo__teco_interface_ssm(SCI_CLEARALL, 0, 0); teco_interface_ssm(SCI_ADDTEXT, teco_cmdline.pc, (sptr_t)macro); - teco_interface_ssm(SCI_ENDUNDOACTION, 0, 0); - - /* - * Must always support undo on global register. - * A undo action should always have been generated. - */ - undo__teco_interface_ssm(SCI_UNDO, 0, 0); /* * FIXME: If we would maintain dot in the command line, @@ -1130,10 +1121,9 @@ teco_state_changedir_done(teco_machine_main_t *ctx, teco_string_t str, GError ** g_autofree gchar *dir = teco_file_expand_path(str.data); if (!*dir) { - teco_qreg_t *qreg = teco_qreg_table_find(&teco_qreg_table_globals, "$HOME", 5); - g_assert(qreg != NULL); teco_string_t home; - if (!qreg->vtable->get_string(qreg, &home.data, &home.len, NULL, error)) + if (!teco_qreg_table_get_string(&teco_qreg_table_globals, "$HOME", + &home.data, &home.len, error)) return NULL; /* @@ -2739,16 +2729,15 @@ teco_state_ecommand_encoding(teco_machine_main_t *ctx, GError **error) if (!converted) return; - teco_interface_ssm(SCI_BEGINUNDOACTION, 0, 0); - teco_interface_ssm(SCI_CLEARALL, 0, 0); - teco_interface_ssm(SCI_APPENDTEXT, converted_len, (sptr_t)converted); - teco_interface_ssm(SCI_ENDUNDOACTION, 0, 0); - teco_ring_dirtify(); - if (teco_current_doc_must_undo()) { undo__teco_interface_ssm(SCI_GOTOPOS, dot_bytes, 0); - undo__teco_interface_ssm(SCI_UNDO, 0, 0); + teco_undo_interface_insert(0, teco_interface_ssm(SCI_GETLENGTH, 0, 0)); + undo__teco_interface_ssm(SCI_CLEARALL, 0, 0); } + + teco_interface_ssm(SCI_CLEARALL, 0, 0); + teco_interface_ssm(SCI_APPENDTEXT, converted_len, (sptr_t)converted); + teco_ring_dirtify(); } if (new_cp == SC_CP_UTF8) { @@ -2988,6 +2977,9 @@ teco_state_insert_initial(teco_machine_main_t *ctx, GError **error) return TRUE; } + gsize dot_bytes = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0); + gsize added_len = 0; + if (teco_interface_ssm(SCI_GETCODEPAGE, 0, 0) == SC_CP_UTF8) { /* detect possible errors before introducing side effects */ for (gint i = args; i > 0; i--) { @@ -2997,12 +2989,12 @@ teco_state_insert_initial(teco_machine_main_t *ctx, GError **error) return FALSE; } } - teco_interface_ssm(SCI_BEGINUNDOACTION, 0, 0); for (gint i = args; i > 0; i--) { /* 4 bytes should be enough, but we better follow the documentation */ gchar buf[6]; gsize len = g_unichar_to_utf8(teco_expressions_peek_num(i-1), buf); teco_interface_ssm(SCI_ADDTEXT, len, (sptr_t)buf); + added_len += len; } } else { /* everything else is a single-byte encoding */ @@ -3013,17 +3005,16 @@ teco_state_insert_initial(teco_machine_main_t *ctx, GError **error) return FALSE; } } - teco_interface_ssm(SCI_BEGINUNDOACTION, 0, 0); for (gint i = args; i > 0; i--) { gchar chr = (gchar)teco_expressions_peek_num(i-1); teco_interface_ssm(SCI_ADDTEXT, 1, (sptr_t)&chr); } + added_len = args; } - teco_interface_ssm(SCI_ENDUNDOACTION, 0, 0); teco_ring_dirtify(); if (teco_current_doc_must_undo()) - undo__teco_interface_ssm(SCI_UNDO, 0, 0); + undo__teco_interface_ssm(SCI_DELETERANGE, dot_bytes, added_len); /* This is done only now because it can _theoretically_ fail. */ for (gint i = 0; i < args; i++) @@ -3041,15 +3032,14 @@ teco_state_insert_process(teco_machine_main_t *ctx, teco_string_t str, { g_assert(new_chars > 0); - teco_interface_ssm(SCI_BEGINUNDOACTION, 0, 0); + if (teco_current_doc_must_undo()) + undo__teco_interface_ssm(SCI_DELETERANGE, + teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0), new_chars); + teco_interface_ssm(SCI_ADDTEXT, new_chars, (sptr_t)(str.data + str.len - new_chars)); - teco_interface_ssm(SCI_ENDUNDOACTION, 0, 0); teco_ring_dirtify(); - if (teco_current_doc_must_undo()) - undo__teco_interface_ssm(SCI_UNDO, 0, 0); - return TRUE; } @@ -3103,8 +3093,11 @@ teco_state_insert_indent_initial(teco_machine_main_t *ctx, GError **error) if (!teco_state_insert_initial(ctx, error)) return FALSE; - teco_interface_ssm(SCI_BEGINUNDOACTION, 0, 0); if (teco_interface_ssm(SCI_GETUSETABS, 0, 0)) { + if (teco_current_doc_must_undo()) + undo__teco_interface_ssm(SCI_DELETERANGE, + teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0), 1); + teco_interface_ssm(SCI_ADDTEXT, 1, (sptr_t)"\t"); /* teco_state_insert_done() expects this */ @@ -3118,16 +3111,16 @@ teco_state_insert_indent_initial(teco_machine_main_t *ctx, GError **error) /* teco_state_insert_done() expects this */ teco_undo_int(teco_ranges[0].to) += len; + if (teco_current_doc_must_undo()) + undo__teco_interface_ssm(SCI_DELETERANGE, + teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0), len); + gchar space = ' '; while (len-- > 0) teco_interface_ssm(SCI_ADDTEXT, 1, (sptr_t)&space); } - teco_interface_ssm(SCI_ENDUNDOACTION, 0, 0); teco_ring_dirtify(); - if (teco_current_doc_must_undo()) - undo__teco_interface_ssm(SCI_UNDO, 0, 0); - return TRUE; } diff --git a/src/error.h b/src/error.h index a848116..1306feb 100644 --- a/src/error.h +++ b/src/error.h @@ -50,6 +50,7 @@ typedef enum { TECO_ERROR_SUBPATTERN, TECO_ERROR_INVALIDBUF, TECO_ERROR_INVALIDQREG, + TECO_ERROR_QREGUNDEF, TECO_ERROR_QREGOPUNSUPPORTED, TECO_ERROR_QREGCONTAINSNULL, TECO_ERROR_EDITINGLOCALQREG, diff --git a/src/file-utils.c b/src/file-utils.c index 8555271..4bf5e6a 100644 --- a/src/file-utils.c +++ b/src/file-utils.c @@ -348,18 +348,13 @@ teco_file_expand_path(const gchar *path) * but this ensures that a proper path is constructed even if * it does (e.g. $HOME is changed later on). * - * FIXME: In the future, it might be possible to remove the entire register. - */ - teco_qreg_t *qreg = teco_qreg_table_find(&teco_qreg_table_globals, "$HOME", 5); - g_assert(qreg != NULL); - - /* * Getting the string should not possible to fail. * The $HOME register should not contain any null-bytes on startup, * but it may have been changed later on. */ g_auto(teco_string_t) home = {NULL, 0}; - if (!qreg->vtable->get_string(qreg, &home.data, &home.len, NULL, NULL) || + if (!teco_qreg_table_get_string(&teco_qreg_table_globals, "$HOME", + &home.data, &home.len, NULL) || teco_string_contains(home, '\0')) return g_strdup(path); g_assert(home.data != NULL); @@ -482,6 +482,7 @@ teco_state_glob_filename_done(teco_machine_main_t *ctx, teco_string_t str, GErro gboolean matching = FALSE; gboolean colon_modified = teco_machine_main_eval_colon(ctx) > 0; + gsize added_len = 0; teco_int_t teco_test_mode; @@ -517,6 +518,8 @@ teco_state_glob_filename_done(teco_machine_main_t *ctx, teco_string_t str, GErro return NULL; } + sptr_t pos = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0); + if (str.len > 0) { /* * Match pattern against provided file name @@ -535,12 +538,10 @@ teco_state_glob_filename_done(teco_machine_main_t *ctx, teco_string_t str, GErro gsize len = strlen(filename); filename[len] = '\n'; - sptr_t pos = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0); teco_undo_int(teco_ranges[0].from) = teco_current_doc_get_dot(); - teco_interface_ssm(SCI_BEGINUNDOACTION, 0, 0); teco_interface_ssm(SCI_ADDTEXT, len+1, (sptr_t)filename); - teco_interface_ssm(SCI_ENDUNDOACTION, 0, 0); + added_len += len+1; teco_int_t to = teco_interface_bytes2glyphs_rel(teco_ranges[0].from, pos, len + 1); teco_undo_int(teco_ranges[0].to) = to; @@ -568,12 +569,9 @@ teco_state_glob_filename_done(teco_machine_main_t *ctx, teco_string_t str, GErro g_autoptr(teco_globber_t) globber; globber = teco_globber_new(pattern_str.data, file_flags); - sptr_t pos = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0); gsize total_len = 0; teco_undo_int(teco_ranges[0].from) = teco_current_doc_get_dot(); - teco_interface_ssm(SCI_BEGINUNDOACTION, 0, 0); - gchar *globbed_filename; while ((globbed_filename = teco_globber_next(globber))) { gsize len = strlen(globbed_filename); @@ -585,13 +583,12 @@ teco_state_glob_filename_done(teco_machine_main_t *ctx, teco_string_t str, GErro */ globbed_filename[len] = '\n'; teco_interface_ssm(SCI_ADDTEXT, len+1, (sptr_t)globbed_filename); + added_len += len+1; g_free(globbed_filename); matching = TRUE; } - teco_interface_ssm(SCI_ENDUNDOACTION, 0, 0); - teco_int_t to = teco_interface_bytes2glyphs_rel(teco_ranges[0].from, pos, total_len); teco_undo_int(teco_ranges[0].to) = to; teco_undo_guint(teco_ranges_count) = 1; @@ -600,11 +597,11 @@ teco_state_glob_filename_done(teco_machine_main_t *ctx, teco_string_t str, GErro if (colon_modified) { teco_expressions_push(teco_bool(matching)); - } else if (matching) { + } else if (added_len) { /* text has been inserted */ teco_ring_dirtify(); if (teco_current_doc_must_undo()) - undo__teco_interface_ssm(SCI_UNDO, 0, 0); + undo__teco_interface_ssm(SCI_DELETERANGE, pos, added_len); } if (!glob_reg->vtable->set_integer(glob_reg, teco_bool(matching), error)) @@ -92,10 +92,9 @@ teco_help_init(GError **error) teco_help_chunk = g_string_chunk_new(32); rb3_reset_tree(&teco_help_tree); - teco_qreg_t *lib_reg = teco_qreg_table_find(&teco_qreg_table_globals, "$SCITECOPATH", 12); - g_assert(lib_reg != NULL); g_auto(teco_string_t) lib_path = {NULL, 0}; - if (!lib_reg->vtable->get_string(lib_reg, &lib_path.data, &lib_path.len, NULL, error)) + if (!teco_qreg_table_get_string(&teco_qreg_table_globals, "$SCITECOPATH", + &lib_path.data, &lib_path.len, error)) return FALSE; /* * FIXME: lib_path may contain null-bytes. diff --git a/src/interface-curses/interface.c b/src/interface-curses/interface.c index db996f5..b304e12 100644 --- a/src/interface-curses/interface.c +++ b/src/interface-curses/interface.c @@ -43,10 +43,6 @@ #include <glib/gprintf.h> #include <glib/gstdio.h> -#ifdef G_OS_UNIX -#include <sys/wait.h> -#endif - #include <curses.h> #ifdef HAVE_TIGETSTR @@ -70,6 +66,7 @@ #include "error.h" #include "view.h" #include "memory.h" +#include "spawn.h" #include "interface.h" #include "curses-utils.h" #include "curses-info-popup.h" @@ -1527,17 +1524,12 @@ teco_interface_set_clipboard(const gchar *name, const gchar *str, gsize str_len, static const gchar reg_name[] = "$SCITECO_CLIPBOARD_SET"; - teco_qreg_t *reg = teco_qreg_table_find(&teco_qreg_table_globals, reg_name, strlen(reg_name)); - if (!reg) { - /* Q-Register could have been removed in the meantime */ - g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED, - "Cannot set clipboard. %s is undefined.", reg_name); - return FALSE; - } - g_auto(teco_string_t) command = {NULL, 0}; - if (!reg->vtable->get_string(reg, &command.data, &command.len, NULL, error)) + if (!teco_qreg_table_get_string(&teco_qreg_table_globals, reg_name, + &command.data, &command.len, error)) { + g_prefix_error(error, "Cannot set clipboard: "); return FALSE; + } if (teco_string_contains(command, '\0')) { teco_error_qregcontainsnull_set(error, reg_name, strlen(reg_name), FALSE); return FALSE; @@ -1549,25 +1541,24 @@ teco_interface_set_clipboard(const gchar *name, const gchar *str, gsize str_len, *sel = get_selection_by_name(name); } + errno = ENOMEM; FILE *pipe = popen(command.data, "w"); if (!pipe) { g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED, - "Cannot spawn process from %s", reg_name); + "Cannot spawn process from %s: %s", reg_name, g_strerror(errno)); return FALSE; } size_t len = fwrite(str, 1, str_len, pipe); - int status = pclose(pipe); - if (status < 0 || !WIFEXITED(status)) { + gint status = pclose(pipe); + if (status < 0) { g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED, - "Error reaping process from %s", reg_name); + "Error running process from %s: %s", reg_name, g_strerror(errno)); return FALSE; } - if (WEXITSTATUS(status) != 0) { - g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED, - "Process from %s returned with exit code %d", - reg_name, WEXITSTATUS(status)); + if (!teco_spawn_check_wait_status(status, error)) { + g_prefix_error(error, "Error running process from %s: ", reg_name); return FALSE; } @@ -1588,17 +1579,12 @@ teco_interface_get_clipboard(const gchar *name, gchar **str, gsize *len, GError static const gchar reg_name[] = "$SCITECO_CLIPBOARD_GET"; - teco_qreg_t *reg = teco_qreg_table_find(&teco_qreg_table_globals, reg_name, strlen(reg_name)); - if (!reg) { - /* Q-Register could have been removed in the meantime */ - g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED, - "Cannot get clipboard. %s is undefined.", reg_name); - return FALSE; - } - g_auto(teco_string_t) command = {NULL, 0}; - if (!reg->vtable->get_string(reg, &command.data, &command.len, NULL, error)) + if (!teco_qreg_table_get_string(&teco_qreg_table_globals, reg_name, + &command.data, &command.len, error)) { + g_prefix_error(error, "Cannot get clipboard: "); return FALSE; + } if (teco_string_contains(command, '\0')) { teco_error_qregcontainsnull_set(error, reg_name, strlen(reg_name), FALSE); return FALSE; @@ -1610,10 +1596,11 @@ teco_interface_get_clipboard(const gchar *name, gchar **str, gsize *len, GError *sel = get_selection_by_name(name); } + errno = ENOMEM; FILE *pipe = popen(command.data, "r"); if (!pipe) { g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED, - "Cannot spawn process from %s", reg_name); + "Cannot spawn process from %s: %s", reg_name, g_strerror(errno)); return FALSE; } @@ -1627,20 +1614,18 @@ teco_interface_get_clipboard(const gchar *name, gchar **str, gsize *len, GError teco_string_append(&ret, buffer, read_len); } while (read_len == sizeof(buffer)); - int status = pclose(pipe); - if (status < 0 || !WIFEXITED(status)) { + gint status = pclose(pipe); + if (status < 0) { g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED, - "Error reaping process from %s", reg_name); + "Error running process from %s: %s", reg_name, g_strerror(errno)); return FALSE; } /* * You may have to add a `|| true` for instance to xclip if it * could fail for empty selections. */ - if (WEXITSTATUS(status) != 0) { - g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED, - "Process from %s returned with exit code %d", - reg_name, WEXITSTATUS(status)); + if (!teco_spawn_check_wait_status(status, error)) { + g_prefix_error(error, "Error running process from %s: ", reg_name); return FALSE; } @@ -1972,7 +1957,7 @@ teco_interface_process_mevent(MEVENT *event, GError **error) * Furthermore due to ncurses bugs the order * of events is arbitrary. * Therefore we ignore BUTTON2_PRESSED and synthesize - * PRESSED and RELEASED evnts on BUTTON2_RELEASED: + * PRESSED and RELEASED events on BUTTON2_RELEASED: */ if (teco_mouse.button == 2) { if (teco_mouse.type == TECO_MOUSE_PRESSED) @@ -2033,7 +2018,7 @@ teco_interface_blocking_getch(void) * If we would reset the mouse mask with every wgetch(), which resets * the internal button state, we would receive bogus BUTTON3_PRESSED events * repeatedly. - * An upstream ncurses patch will probably be merged soon. + * An upstream ncurses patch has been merged since NCURSES_VERSION_PATCH >= 20250913. */ static gboolean old_mousekey = FALSE; gboolean new_mousekey = (teco_ed & TECO_ED_MOUSEKEY) != 0; diff --git a/src/interface-gtk/interface.c b/src/interface-gtk/interface.c index 1f6259f..b2d4788 100644 --- a/src/interface-gtk/interface.c +++ b/src/interface-gtk/interface.c @@ -1210,11 +1210,9 @@ teco_interface_handle_scroll(GdkEventScroll *event, GError **error) gboolean teco_interface_event_loop(GError **error) { - teco_qreg_t *scitecoconfig_reg = teco_qreg_table_find(&teco_qreg_table_globals, "$SCITECOCONFIG", 14); - g_assert(scitecoconfig_reg != NULL); g_auto(teco_string_t) scitecoconfig = {NULL, 0}; - if (!scitecoconfig_reg->vtable->get_string(scitecoconfig_reg, - &scitecoconfig.data, &scitecoconfig.len, NULL, error)) + if (!teco_qreg_table_get_string(&teco_qreg_table_globals, "$SCITECOCONFIG", + &scitecoconfig.data, &scitecoconfig.len, error)) return FALSE; if (teco_string_contains(scitecoconfig, '\0')) { g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED, diff --git a/src/interface.h b/src/interface.h index 9c3d31e..20d9773 100644 --- a/src/interface.h +++ b/src/interface.h @@ -286,6 +286,12 @@ teco_interface_get_character(gsize pos, gsize len) return teco_view_get_character(teco_interface_current_view, pos, len); } +static inline void +teco_undo_interface_insert(gsize pos, gsize len) +{ + return teco_undo_view_insert(teco_interface_current_view, pos, len); +} + /* * The following functions are here for lack of a better place. * They could also be in sciteco.h, but only if declared as non-inline @@ -444,16 +444,9 @@ teco_lsp_launch(GError **error) G_SPAWN_STDERR_TO_DEV_NULL; static const gchar lsp_reg_name[] = "$SCITECO_LSP"; - teco_qreg_t *reg = teco_qreg_table_find(&teco_qreg_table_globals, - lsp_reg_name, strlen(lsp_reg_name)); - if (!reg) { - g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED, - "Q-Register %s is undefined.", lsp_reg_name); - return FALSE; - } - g_auto(teco_string_t) command = {NULL, 0}; - if (!reg->vtable->get_string(reg, &command.data, &command.len, NULL, error)) + if (!teco_qreg_table_get_string(&teco_qreg_table_globals, lsp_reg_name, + &command.data, &command.len, error)) return FALSE; if (teco_string_contains(command, '\0')) { teco_error_qregcontainsnull_set(error, lsp_reg_name, strlen(lsp_reg_name), FALSE); @@ -497,8 +490,8 @@ teco_lsp_launch(GError **error) g_auto(teco_string_t) root = {NULL, 0}; static const gchar root_reg_name[] = "$SCITECO_LSP_ROOT"; - reg = teco_qreg_table_find(&teco_qreg_table_globals, - root_reg_name, strlen(root_reg_name)); + teco_qreg_t *reg = teco_qreg_table_find(&teco_qreg_table_globals, + root_reg_name, strlen(root_reg_name)); if (reg) { if (!reg->vtable->get_string(reg, &root.data, &root.len, NULL, error)) return FALSE; @@ -600,8 +600,6 @@ main(int argc, char **argv) teco_qreg_table_replace(&teco_qreg_table_globals, teco_qreg_plain_new("\e", 1)); teco_undo_enabled = TRUE; - teco_ring_set_scintilla_undo(TRUE); - teco_view_set_scintilla_undo(teco_qreg_view, TRUE); /* * FIXME: Perhaps we should simply call teco_cmdline_init() and @@ -633,9 +631,6 @@ main(int argc, char **argv) */ teco_undo_enabled = FALSE; teco_undo_clear(); - /* also empties all Scintilla undo buffers */ - teco_ring_set_scintilla_undo(FALSE); - teco_view_set_scintilla_undo(teco_qreg_view, FALSE); if (!teco_ed_hook(TECO_ED_HOOK_QUIT, &error)) goto cleanup; diff --git a/src/memory.c b/src/memory.c index 97bc627..2a3b51a 100644 --- a/src/memory.c +++ b/src/memory.c @@ -326,6 +326,18 @@ free(void *ptr) dlfree(ptr); } +void __attribute__((used)) +free_sized(void *ptr, size_t size) +{ + free(ptr); +} + +void __attribute__((used)) +free_aligned_sized(void *ptr, size_t alignment, size_t size) +{ + free(ptr); +} + void * __attribute__((used)) calloc(size_t nmemb, size_t size) { diff --git a/src/move-commands.c b/src/move-commands.c index 9ef893e..868bb19 100644 --- a/src/move-commands.c +++ b/src/move-commands.c @@ -472,14 +472,10 @@ teco_state_start_delete_words(teco_machine_main_t *ctx, const gchar *cmd, gint f if (start_pos < pos) teco_current_doc_set_dot(teco_interface_bytes2glyphs_absdot(start_pos)); - teco_interface_ssm(SCI_BEGINUNDOACTION, 0, 0); - teco_interface_ssm(SCI_DELETERANGE, start_pos, end_pos-start_pos); - teco_interface_ssm(SCI_ENDUNDOACTION, 0, 0); + if (teco_current_doc_must_undo()) + teco_undo_interface_insert(start_pos, end_pos-start_pos); - if (teco_current_doc_must_undo()) { - undo__teco_interface_ssm(SCI_GOTOPOS, pos, 0); - undo__teco_interface_ssm(SCI_UNDO, 0, 0); - } + teco_interface_ssm(SCI_DELETERANGE, start_pos, end_pos-start_pos); teco_ring_dirtify(); } @@ -562,17 +558,10 @@ teco_state_start_kill(teco_machine_main_t *ctx, const gchar *cmd, gboolean by_li if (len == 0 || teco_is_failure(rc)) return TRUE; - if (teco_current_doc_must_undo()) { - undo__teco_interface_ssm(SCI_GOTOPOS, pos, 0); - undo__teco_interface_ssm(SCI_UNDO, 0, 0); - } + if (teco_current_doc_must_undo()) + teco_undo_interface_insert(from, len); - /* - * Should always generate an undo action. - */ - teco_interface_ssm(SCI_BEGINUNDOACTION, 0, 0); teco_interface_ssm(SCI_DELETERANGE, from, len); - teco_interface_ssm(SCI_ENDUNDOACTION, 0, 0); teco_ring_dirtify(); return TRUE; diff --git a/src/parser.c b/src/parser.c index 8b02764..45e1e3c 100644 --- a/src/parser.c +++ b/src/parser.c @@ -487,6 +487,7 @@ static teco_state_t teco_state_stringbuilding_lower; static teco_state_t teco_state_stringbuilding_upper; static teco_state_t teco_state_stringbuilding_ctle; +static teco_state_t teco_state_stringbuilding_ctle_cr; static teco_state_t teco_state_stringbuilding_ctle_num; static teco_state_t teco_state_stringbuilding_ctle_u; static teco_state_t teco_state_stringbuilding_ctle_code; @@ -714,6 +715,8 @@ teco_state_stringbuilding_ctle_input(teco_machine_stringbuilding_t *ctx, gunicha teco_state_t *next; switch (teco_ascii_toupper(chr)) { + case '\r': return &teco_state_stringbuilding_ctle_cr; + case '\n': return &teco_state_stringbuilding_start; case '\\': next = &teco_state_stringbuilding_ctle_num; break; case 'U': next = &teco_state_stringbuilding_ctle_u; break; case '<': next = &teco_state_stringbuilding_ctle_code; break; @@ -747,6 +750,17 @@ static TECO_DEFINE_STATE_CASEINSENSITIVE(teco_state_stringbuilding_ctle, gboolean teco_state_stringbuilding_qreg_process_edit_cmd(teco_machine_stringbuilding_t *ctx, teco_machine_t *parent_ctx, gunichar chr, GError **error); +static teco_state_t * +teco_state_stringbuilding_ctle_cr_input(teco_machine_stringbuilding_t *ctx, gunichar chr, GError **error) +{ + return chr == '\n' ? &teco_state_stringbuilding_start /* DOS linebreak */ + : teco_state_stringbuilding_start_input(ctx, chr, error); /* Mac linebreak */ +} + +static TECO_DEFINE_STATE(teco_state_stringbuilding_ctle_cr, + .input_cb = (teco_state_input_cb_t)teco_state_stringbuilding_ctle_cr_input +); + /** * @interface TECO_DEFINE_STATE_STRINGBUILDING_QREG * @implements TECO_DEFINE_STATE diff --git a/src/qreg-commands.c b/src/qreg-commands.c index a6390a1..0c4c8d4 100644 --- a/src/qreg-commands.c +++ b/src/qreg-commands.c @@ -563,13 +563,11 @@ teco_state_getqregstring_got_register(teco_machine_main_t *ctx, teco_qreg_t *qre sptr_t pos = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0); if (str.len > 0) { - teco_interface_ssm(SCI_BEGINUNDOACTION, 0, 0); teco_interface_ssm(SCI_ADDTEXT, str.len, (sptr_t)str.data); - teco_interface_ssm(SCI_ENDUNDOACTION, 0, 0); teco_ring_dirtify(); if (teco_current_doc_must_undo()) - undo__teco_interface_ssm(SCI_UNDO, 0, 0); + undo__teco_interface_ssm(SCI_DELETERANGE, pos, str.len); } teco_undo_int(teco_ranges[0].from) = teco_current_doc_get_dot(); @@ -835,17 +833,10 @@ teco_state_copytoqreg_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg, /* * If @-modified, cut into the register */ - if (teco_current_doc_must_undo()) { - undo__teco_interface_ssm(SCI_GOTOPOS, pos, 0); - undo__teco_interface_ssm(SCI_UNDO, 0, 0); - } + if (teco_current_doc_must_undo()) + teco_undo_interface_insert(from, len); - /* - * Should always generate an undo action. - */ - teco_interface_ssm(SCI_BEGINUNDOACTION, 0, 0); teco_interface_ssm(SCI_DELETERANGE, from, len); - teco_interface_ssm(SCI_ENDUNDOACTION, 0, 0); teco_ring_dirtify(); if (from+len < pos) @@ -188,7 +188,9 @@ teco_qreg_plain_append_string(teco_qreg_t *qreg, const gchar *str, gsize len, GE teco_doc_undo_edit(&teco_qreg_current->string); teco_doc_undo_reset(&qreg->string); - undo__teco_view_ssm(teco_qreg_view, SCI_UNDO, 0, 0); + + sptr_t pos = teco_view_ssm(teco_qreg_view, SCI_GETCURRENTPOS, 0, 0); + undo__teco_view_ssm(teco_qreg_view, SCI_DELETERANGE, pos, len); } if (teco_qreg_current) @@ -196,9 +198,7 @@ teco_qreg_plain_append_string(teco_qreg_t *qreg, const gchar *str, gsize len, GE teco_doc_edit(&qreg->string, teco_default_codepage()); - teco_view_ssm(teco_qreg_view, SCI_BEGINUNDOACTION, 0, 0); teco_view_ssm(teco_qreg_view, SCI_APPENDTEXT, len, (sptr_t)str); - teco_view_ssm(teco_qreg_view, SCI_ENDUNDOACTION, 0, 0); if (teco_qreg_current) teco_doc_edit(&teco_qreg_current->string, 0); @@ -469,12 +469,12 @@ teco_qreg_external_edit(teco_qreg_t *qreg, GError **error) !qreg->vtable->get_string(qreg, &str.data, &str.len, NULL, error)) return FALSE; - teco_view_ssm(teco_qreg_view, SCI_BEGINUNDOACTION, 0, 0); + teco_undo_view_insert(teco_qreg_view, 0, + teco_view_ssm(teco_qreg_view, SCI_GETLENGTH, 0, 0)); teco_view_ssm(teco_qreg_view, SCI_CLEARALL, 0, 0); + undo__teco_view_ssm(teco_qreg_view, SCI_CLEARALL, 0, 0); teco_view_ssm(teco_qreg_view, SCI_ADDTEXT, str.len, (sptr_t)str.data); - teco_view_ssm(teco_qreg_view, SCI_ENDUNDOACTION, 0, 0); - undo__teco_view_ssm(teco_qreg_view, SCI_UNDO, 0, 0); return TRUE; } @@ -586,12 +586,11 @@ teco_qreg_external_save(teco_qreg_t *qreg, const gchar *filename, GError **error if (!qreg->vtable->get_string(qreg, &str.data, &str.len, NULL, error)) return FALSE; - teco_view_ssm(teco_qreg_view, SCI_BEGINUNDOACTION, 0, 0); + teco_undo_view_insert(teco_qreg_view, 0, + teco_view_ssm(teco_qreg_view, SCI_GETLENGTH, 0, 0)); teco_view_ssm(teco_qreg_view, SCI_CLEARALL, 0, 0); + undo__teco_view_ssm(teco_qreg_view, SCI_CLEARALL, 0, 0); teco_view_ssm(teco_qreg_view, SCI_ADDTEXT, str.len, (sptr_t)str.data); - teco_view_ssm(teco_qreg_view, SCI_ENDUNDOACTION, 0, 0); - - undo__teco_view_ssm(teco_qreg_view, SCI_UNDO, 0, 0); gboolean ret = teco_view_save(teco_qreg_view, filename, error); @@ -16,6 +16,8 @@ */ #pragma once +#include <string.h> + #include <glib.h> //#include <rb3ptr.h> @@ -24,6 +26,7 @@ #include "view.h" #include "doc.h" #include "undo.h" +#include "error.h" #include "string-utils.h" #include "rb3str.h" @@ -212,6 +215,36 @@ teco_qreg_table_find(teco_qreg_table_t *table, const gchar *name, gsize len) return (teco_qreg_t *)teco_rb3str_find(&table->tree, TRUE, name, len); } +/** @memberof teco_qreg_table_t */ +static inline gboolean +teco_qreg_table_get_string(teco_qreg_table_t *table, const gchar *name, + gchar **str, gsize *len, GError **error) +{ + teco_qreg_t *reg = teco_qreg_table_find(table, name, strlen(name)); + if (!reg) { + g_set_error(error, TECO_ERROR, TECO_ERROR_QREGUNDEF, + "Q-Register \"%s\" is undefined.", name); + return FALSE; + } + + return reg->vtable->get_string(reg, str, len, NULL, error); +} + +/** @memberof teco_qreg_table_t */ +static inline gboolean +teco_qreg_table_get_integer(teco_qreg_table_t *table, const gchar *name, + teco_int_t *ret, GError **error) +{ + teco_qreg_t *reg = teco_qreg_table_find(table, name, strlen(name)); + if (!reg) { + g_set_error(error, TECO_ERROR, TECO_ERROR_QREGUNDEF, + "Q-Register \"%s\" is undefined.", name); + return FALSE; + } + + return reg->vtable->get_integer(reg, ret, error); +} + teco_qreg_t *teco_qreg_table_edit_name(teco_qreg_table_t *table, const gchar *name, gsize len, GError **error); @@ -503,15 +503,6 @@ teco_ring_undo_close(void) undo__teco_ring_remove_buffer(teco_ring_current); } -void -teco_ring_set_scintilla_undo(gboolean state) -{ - for (teco_tailq_entry_t *cur = teco_ring_head.first; cur != NULL; cur = cur->next) { - teco_buffer_t *buffer = (teco_buffer_t *)cur; - teco_view_set_scintilla_undo(buffer->view, state); - } -} - gboolean teco_ring_sync_lsp(GError **error) { @@ -783,7 +774,7 @@ teco_state_read_file_done(teco_machine_main_t *ctx, teco_string_t str, GError ** teco_ring_dirtify(); if (teco_current_doc_must_undo()) - undo__teco_interface_ssm(SCI_UNDO, 0, 0); + undo__teco_interface_ssm(SCI_DELETERANGE, from, to-from); } return &teco_state_start; @@ -121,8 +121,6 @@ teco_ring_undo_edit(void) gboolean teco_ring_close(teco_buffer_t *buffer, GError **error); void teco_ring_undo_close(void); -void teco_ring_set_scintilla_undo(gboolean state); - gboolean teco_ring_sync_lsp(GError **error); void teco_ring_cleanup(void); diff --git a/src/search.c b/src/search.c index 1b5026a..31a551e 100644 --- a/src/search.c +++ b/src/search.c @@ -152,14 +152,6 @@ teco_state_search_initial(teco_machine_main_t *ctx, GError **error) teco_error_range_set(error, "S"); return FALSE; } - } else if (v2 == 0) { - /* - * NOTE: Video TECO always succeeds on `0S...$`. - * TECOC however fails just like we do. - */ - g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED, - "The search counter must not be zero."); - return FALSE; } else { /* search for v2-th occurrence */ teco_search_parameters.count = (gint)v2; @@ -172,6 +164,16 @@ teco_state_search_initial(teco_machine_main_t *ctx, GError **error) } } + if (!teco_search_parameters.count) { + /* + * NOTE: Video TECO always succeeds on `0S...$`. + * TECOC however fails just like we do. + */ + g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED, + "The search counter must not be zero."); + return FALSE; + } + teco_search_parameters.from_buffer = teco_qreg_current ? NULL : teco_ring_current; teco_search_parameters.to_buffer = NULL; return TRUE; @@ -926,10 +928,9 @@ teco_state_search_process(teco_machine_main_t *ctx, teco_string_t str, gsize new { gint cflags = REG_ADVANCED; - teco_qreg_t *reg = teco_qreg_table_find(ctx->qreg_table_locals, "\x18", 1); /* ^X */ - g_assert(reg != NULL); teco_bool_t search_mode; - if (!reg->vtable->get_integer(reg, &search_mode, error)) + if (!teco_qreg_table_get_integer(ctx->qreg_table_locals, "\x18", /* ^X */ + &search_mode, error)) return FALSE; if (teco_is_failure(search_mode)) cflags |= REG_ICASE; @@ -1251,7 +1252,17 @@ teco_state_search_all_initial(teco_machine_main_t *ctx, GError **error) } } - if (teco_search_parameters.count >= 0) { + if (!teco_search_parameters.count) { + /* + * NOTE: Video TECO always succeeds on `0N...$`. + * TECOC however fails just like we do. + */ + g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED, + "The search counter must not be zero."); + return FALSE; + } + + if (teco_search_parameters.count > 0) { teco_search_parameters.from = teco_search_parameters.pos; teco_search_parameters.to = teco_interface_ssm(SCI_GETLENGTH, 0, 0); } else { @@ -1332,12 +1343,9 @@ teco_state_search_kill_done(teco_machine_main_t *ctx, teco_string_t str, GError if (ctx->flags.mode > TECO_MODE_NORMAL) return &teco_state_start; - teco_qreg_t *search_reg = teco_qreg_table_find(&teco_qreg_table_globals, "_", 1); - g_assert(search_reg != NULL); - teco_int_t search_state; if (!teco_state_search_done(ctx, str, error) || - !search_reg->vtable->get_integer(search_reg, &search_state, error)) + !teco_qreg_table_get_integer(&teco_qreg_table_globals, "_", &search_state, error)) return NULL; if (teco_is_failure(search_state)) @@ -1345,7 +1353,6 @@ teco_state_search_kill_done(teco_machine_main_t *ctx, teco_string_t str, GError sptr_t pos = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0); - teco_interface_ssm(SCI_BEGINUNDOACTION, 0, 0); if (teco_search_parameters.pos < pos) { /* kill forwards */ sptr_t anchor = teco_interface_ssm(SCI_GETANCHOR, 0, 0); @@ -1353,17 +1360,13 @@ teco_state_search_kill_done(teco_machine_main_t *ctx, teco_string_t str, GError teco_interface_bytes2glyphs_absdot(teco_search_parameters.pos); if (teco_current_doc_must_undo()) - undo__teco_interface_ssm(SCI_GOTOPOS, pos, 0); + teco_undo_interface_insert(teco_search_parameters.pos, + anchor - teco_search_parameters.pos); teco_interface_ssm(SCI_GOTOPOS, anchor, 0); teco_interface_ssm(SCI_DELETERANGE, teco_search_parameters.pos, anchor - teco_search_parameters.pos); - /* NOTE: An undo action is not always created. */ - if (teco_current_doc_must_undo() && - teco_search_parameters.pos != anchor) - undo__teco_interface_ssm(SCI_UNDO, 0, 0); - /* fix up ranges (^Y) */ for (guint i = 0; i < teco_ranges_count; i++) { teco_ranges[i].from -= len_glyphs; @@ -1376,14 +1379,10 @@ teco_state_search_kill_done(teco_machine_main_t *ctx, teco_string_t str, GError /* kill backwards */ teco_current_doc_set_dot(teco_interface_bytes2glyphs_absdot(pos)); + if (teco_current_doc_must_undo()) + teco_undo_interface_insert(pos, teco_search_parameters.pos - pos); teco_interface_ssm(SCI_DELETERANGE, pos, teco_search_parameters.pos - pos); - - /* NOTE: An undo action is not always created. */ - if (teco_current_doc_must_undo() && - teco_search_parameters.pos != pos) - undo__teco_interface_ssm(SCI_UNDO, 0, 0); } - teco_interface_ssm(SCI_ENDUNDOACTION, 0, 0); teco_ring_dirtify(); return &teco_state_start; @@ -1421,25 +1420,22 @@ teco_state_search_delete_done(teco_machine_main_t *ctx, teco_string_t str, GErro if (ctx->flags.mode > TECO_MODE_NORMAL) return &teco_state_start; - teco_qreg_t *search_reg = teco_qreg_table_find(&teco_qreg_table_globals, "_", 1); - g_assert(search_reg != NULL); - teco_int_t search_state; if (!teco_state_search_done(ctx, str, error) || - !search_reg->vtable->get_integer(search_reg, &search_state, error)) + !teco_qreg_table_get_integer(&teco_qreg_table_globals, "_", &search_state, error)) return NULL; if (teco_is_success(search_state)) { sptr_t anchor = teco_interface_ssm(SCI_GETANCHOR, 0, 0); teco_current_doc_set_dot(teco_interface_bytes2glyphs_absdot(anchor)); - teco_interface_ssm(SCI_BEGINUNDOACTION, 0, 0); + if (teco_current_doc_must_undo()) { + gsize start = teco_interface_ssm(SCI_GETSELECTIONSTART, 0, 0); + teco_undo_interface_insert(start, teco_interface_ssm(SCI_GETSELECTIONEND, 0, 0) - start); + } + teco_interface_ssm(SCI_REPLACESEL, 0, (sptr_t)""); - teco_interface_ssm(SCI_ENDUNDOACTION, 0, 0); teco_ring_dirtify(); - - if (teco_current_doc_must_undo()) - undo__teco_interface_ssm(SCI_UNDO, 0, 0); } return &teco_state_start; @@ -1507,12 +1503,9 @@ teco_state_replace_done(teco_machine_main_t *ctx, teco_string_t str, GError **er if (ctx->flags.mode > TECO_MODE_NORMAL) return &teco_state_replace_ignore; - teco_qreg_t *search_reg = teco_qreg_table_find(&teco_qreg_table_globals, "_", 1); - g_assert(search_reg != NULL); - teco_int_t search_state; if (!teco_state_search_delete_done(ctx, str, error) || - !search_reg->vtable->get_integer(search_reg, &search_state, error)) + !teco_qreg_table_get_integer(&teco_qreg_table_globals, "_", &search_state, error)) return NULL; return teco_is_success(search_state) ? &teco_state_replace_insert @@ -1615,12 +1608,9 @@ teco_state_replace_default_done(teco_machine_main_t *ctx, teco_string_t str, GEr if (ctx->flags.mode > TECO_MODE_NORMAL) return &teco_state_replace_default_ignore; - teco_qreg_t *search_reg = teco_qreg_table_find(&teco_qreg_table_globals, "_", 1); - g_assert(search_reg != NULL); - teco_int_t search_state; if (!teco_state_search_delete_done(ctx, str, error) || - !search_reg->vtable->get_integer(search_reg, &search_state, error)) + !teco_qreg_table_get_integer(&teco_qreg_table_globals, "_", &search_state, error)) return NULL; return teco_is_success(search_state) ? &teco_state_replace_default_insert diff --git a/src/spawn.c b/src/spawn.c index 6289830..933d8f4 100644 --- a/src/spawn.c +++ b/src/spawn.c @@ -49,17 +49,6 @@ #include "error.h" #include "spawn.h" -/* - * Glib v2.70 deprecates g_spawn_check_exit_status(), - * renaming it to g_spawn_check_wait_status(). - * This leaves no way to work on both new and old versions without warnings. - */ -#if GLIB_CHECK_VERSION(2,70,0) -#define teco_spawn_check_wait_status g_spawn_check_wait_status -#else -#define teco_spawn_check_wait_status g_spawn_check_exit_status -#endif - static void teco_spawn_child_watch_cb(GPid pid, gint status, gpointer data); static gboolean teco_spawn_stdin_watch_cb(GIOChannel *chan, GIOCondition condition, gpointer data); @@ -125,10 +114,9 @@ teco_parse_shell_command_line(const gchar *cmdline, GError **error) #ifdef G_OS_WIN32 if (!(teco_ed & TECO_ED_SHELLEMU)) { - teco_qreg_t *reg = teco_qreg_table_find(&teco_qreg_table_globals, "$COMSPEC", 8); - g_assert(reg != NULL); teco_string_t comspec; - if (!reg->vtable->get_string(reg, &comspec.data, &comspec.len, NULL, error)) + if (!teco_qreg_table_get_string(&teco_qreg_table_globals, "$COMSPEC", + &comspec.data, &comspec.len, error)) return NULL; if (teco_string_contains(comspec, '\0')) { teco_string_clear(&comspec); @@ -145,10 +133,9 @@ teco_parse_shell_command_line(const gchar *cmdline, GError **error) } #elif defined(G_OS_UNIX) if (!(teco_ed & TECO_ED_SHELLEMU)) { - teco_qreg_t *reg = teco_qreg_table_find(&teco_qreg_table_globals, "$SHELL", 6); - g_assert(reg != NULL); teco_string_t shell; - if (!reg->vtable->get_string(reg, &shell.data, &shell.len, NULL, error)) + if (!teco_qreg_table_get_string(&teco_qreg_table_globals, "$SHELL", + &shell.data, &shell.len, error)) return NULL; if (teco_string_contains(shell, '\0')) { teco_string_clear(&shell); @@ -435,10 +422,16 @@ teco_state_execute_done(teco_machine_main_t *ctx, teco_string_t str, GError **er teco_interface_ssm(SCI_GOTOPOS, teco_spawn_ctx.to, 0); } - teco_interface_ssm(SCI_BEGINUNDOACTION, 0, 0); teco_spawn_ctx.start = teco_spawn_ctx.from; g_main_loop_run(teco_spawn_ctx.mainloop); if (!teco_spawn_ctx.register_argument) { + if (teco_current_doc_must_undo()) { + undo__teco_interface_ssm(SCI_DELETERANGE, teco_spawn_ctx.to, + teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0)-teco_spawn_ctx.to); + teco_undo_interface_insert(teco_spawn_ctx.from, + teco_spawn_ctx.to - teco_spawn_ctx.from); + } + teco_interface_ssm(SCI_DELETERANGE, teco_spawn_ctx.from, teco_spawn_ctx.to - teco_spawn_ctx.from); @@ -450,7 +443,6 @@ teco_state_execute_done(teco_machine_main_t *ctx, teco_string_t str, GError **er teco_current_doc_set_dot(to); } - teco_interface_ssm(SCI_ENDUNDOACTION, 0, 0); if (teco_spawn_ctx.register_argument) { if (teco_spawn_ctx.stdout_reader.eol_style >= 0) { @@ -459,9 +451,6 @@ teco_state_execute_done(teco_machine_main_t *ctx, teco_string_t str, GError **er teco_spawn_ctx.stdout_reader.eol_style); } } else if (teco_spawn_ctx.from != teco_spawn_ctx.to || teco_spawn_ctx.text_added) { - /* undo action has only been created if it changed anything */ - if (teco_current_doc_must_undo()) - undo__teco_interface_ssm(SCI_UNDO, 0, 0); teco_ring_dirtify(); } diff --git a/src/spawn.h b/src/spawn.h index 5d2a5a4..bac27ca 100644 --- a/src/spawn.h +++ b/src/spawn.h @@ -16,8 +16,21 @@ */ #pragma once +#include <glib.h> + #include "parser.h" +/* + * Glib v2.70 deprecates g_spawn_check_exit_status(), + * renaming it to g_spawn_check_wait_status(). + * This leaves no way to work on both new and old versions without warnings. + */ +#if GLIB_CHECK_VERSION(2,70,0) +#define teco_spawn_check_wait_status g_spawn_check_wait_status +#else +#define teco_spawn_check_wait_status g_spawn_check_exit_status +#endif + gchar **teco_parse_shell_command_line(const gchar *cmdline, GError **error); extern teco_state_t teco_state_execute; @@ -65,10 +65,9 @@ teco_view_setup(teco_view_t *ctx) SC_MOD_INSERTTEXT | SC_MOD_BEFOREDELETE, 0); /* - * Start with or without undo collection, - * depending on teco_undo_enabled. + * We are exclusively using SciTECO's undo tokens. */ - teco_view_ssm(ctx, SCI_SETUNDOCOLLECTION, teco_undo_enabled, 0); + teco_view_ssm(ctx, SCI_SETUNDOCOLLECTION, FALSE, 0); teco_view_ssm(ctx, SCI_SETFOCUS, TRUE, 0); @@ -248,7 +247,6 @@ teco_view_load_from_channel(teco_view_t *ctx, GIOChannel *channel, teco_view_ssm(ctx, SCI_RELEASELINECHARACTERINDEX, SC_LINECHARACTERINDEX_UTF32, 0); - teco_view_ssm(ctx, SCI_BEGINUNDOACTION, 0, 0); if (clear) { teco_view_ssm(ctx, SCI_CLEARALL, 0, 0); @@ -322,8 +320,6 @@ teco_view_load_from_channel(teco_view_t *ctx, GIOChannel *channel, "Inconsistent EOL styles normalized"); cleanup: - teco_view_ssm(ctx, SCI_ENDUNDOACTION, 0, 0); - if (cp == SC_CP_UTF8) teco_view_ssm(ctx, SCI_ALLOCATELINECHARACTERINDEX, SC_LINECHARACTERINDEX_UTF32, 0); @@ -835,6 +831,56 @@ teco_view_get_character(teco_view_t *ctx, gsize pos, gsize len) return rc < 0 ? rc-1 : rc; } +typedef struct { + teco_view_t *view; + /** dot in bytes before the deletion */ + gsize dot_bytes; + gsize position, length; + gchar text[]; +} teco_undo_view_insert_t; + +static void +teco_undo_view_insert_action(teco_undo_view_insert_t *ctx, gboolean run) +{ + if (!run) + return; + teco_view_ssm(ctx->view, SCI_GOTOPOS, ctx->position, 0); + teco_view_ssm(ctx->view, SCI_ADDTEXT, ctx->length, (sptr_t)ctx->text); + teco_view_ssm(ctx->view, SCI_GOTOPOS, ctx->dot_bytes, 0); +} + +/** + * During undo, insert a string from the given view. + * This can be used to undo text deletions. + * + * @param view The view that contains the string + * @param pos Beginning of string in bytes + * @param len Length of string in bytes + */ +void +teco_undo_view_insert(teco_view_t *view, gsize pos, gsize len) +{ + if (!len) + return; + + teco_undo_view_insert_t *ctx; + ctx = teco_undo_push_size((teco_undo_action_t)teco_undo_view_insert_action, + sizeof(*ctx) + len + 1); + if (!ctx) + return; + + ctx->view = view; + ctx->dot_bytes = teco_view_ssm(view, SCI_GETCURRENTPOS, 0, 0); + ctx->position = pos; + ctx->length = len; + + struct Sci_TextRangeFull range = { + .chrg = {pos, pos + len}, + .lpstrText = ctx->text + }; + teco_view_ssm(view, SCI_GETTEXTRANGEFULL, 0, (sptr_t)&range); +} + void teco_view_process_notify(teco_view_t *ctx, const SCNotification *notify) { @@ -44,14 +44,6 @@ void undo__teco_view_ssm(teco_view_t *, unsigned int, uptr_t, sptr_t); void teco_view_set_representations(teco_view_t *ctx); -/** @memberof teco_view_t */ -static inline void -teco_view_set_scintilla_undo(teco_view_t *ctx, gboolean state) -{ - teco_view_ssm(ctx, SCI_EMPTYUNDOBUFFER, 0, 0); - teco_view_ssm(ctx, SCI_SETUNDOCOLLECTION, state, 0); -} - gboolean teco_view_load_from_channel(teco_view_t *ctx, GIOChannel *channel, gboolean clear, GError **error); gboolean teco_view_load_from_file(teco_view_t *ctx, const gchar *filename, @@ -92,4 +84,6 @@ teco_int_t teco_view_bytes2glyphs_rel(teco_view_t *ctx, teco_int_t pos_glyphs, teco_int_t teco_view_get_character(teco_view_t *ctx, gsize pos, gsize len); +void teco_undo_view_insert(teco_view_t *view, gsize pos, gsize len); + void teco_view_process_notify(teco_view_t *ctx, const SCNotification *notify); diff --git a/tests/testsuite.at b/tests/testsuite.at index 49b0673..4378ca0 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -138,6 +138,8 @@ TE_CHECK([[@I/X/ H@FR{X}/12345/ Z-5"N(0/0)']], 0, ignore, ignore) TE_CHECK([[@Ia^EQa(0/0)a]], 0, ignore, ignore) # Video-TECO-like syntax - might change in the future TE_CHECK([[@I/^E<65>^E<0x41>^E<0101>/ <-A:; -A-^^A"N(0/0)' R>]], 0, ignore, ignore) +TE_CHECK([[@I/01^E +23/ .-4"N(0/0)']], 0, ignore, ignore) # TODO: More string building constructs AT_CLEANUP @@ -293,8 +295,9 @@ TE_CHECK([[@I/АБВГД/J -^X :@S/в/"S(0/0)']], 0, ignore, ignore) TE_CHECK([[-^X @^Um{^X} Mm-0"N(0/0)']], 0, ignore, ignore) # Anchored search TE_CHECK([[@I/XYZ/ J ::@S/X/"F(0/0)' H::@S/Z/"S(0/0)']], 0, ignore, ignore) -# 0-th searches: must always fail +# 0-th searches: must always fail, but not crash TE_CHECK([[@I/XYZ/ J 0@S/XYZ/]], 1, ignore, ignore) +TE_CHECK([[@I/XYZ/ J 0@N/XYZ/]], 1, ignore, ignore) AT_CLEANUP AT_SETUP([Searches over buffer boundaries]) |
