aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--doc/sciteco.7.template5
-rw-r--r--freebsd/Makefile5
-rw-r--r--src/Makefile.am8
-rw-r--r--src/cmdline.c9
-rw-r--r--src/core-commands.c73
-rw-r--r--src/error.h1
-rw-r--r--src/file-utils.c9
-rw-r--r--src/glob.c17
-rw-r--r--src/help.c5
-rw-r--r--src/interface-curses/interface.c65
-rw-r--r--src/interface-gtk/interface.c6
-rw-r--r--src/interface.h6
-rw-r--r--src/lsp.c15
-rw-r--r--src/main.c5
-rw-r--r--src/memory.c12
-rw-r--r--src/move-commands.c21
-rw-r--r--src/parser.c14
-rw-r--r--src/qreg-commands.c15
-rw-r--r--src/qreg.c19
-rw-r--r--src/qreg.h33
-rw-r--r--src/ring.c11
-rw-r--r--src/ring.h2
-rw-r--r--src/search.c82
-rw-r--r--src/spawn.c33
-rw-r--r--src/spawn.h13
-rw-r--r--src/view.c58
-rw-r--r--src/view.h10
-rw-r--r--tests/testsuite.at5
29 files changed, 292 insertions, 266 deletions
diff --git a/.gitignore b/.gitignore
index 6447f82..2a08d12 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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);
diff --git a/src/glob.c b/src/glob.c
index cb571c4..c1efe47 100644
--- a/src/glob.c
+++ b/src/glob.c
@@ -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))
diff --git a/src/help.c b/src/help.c
index b7b5fd0..db8a974 100644
--- a/src/help.c
+++ b/src/help.c
@@ -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
diff --git a/src/lsp.c b/src/lsp.c
index 2c1f89b..ff7ddc7 100644
--- a/src/lsp.c
+++ b/src/lsp.c
@@ -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;
diff --git a/src/main.c b/src/main.c
index 29ef024..43f3dc4 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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)
diff --git a/src/qreg.c b/src/qreg.c
index 559373c..9e19202 100644
--- a/src/qreg.c
+++ b/src/qreg.c
@@ -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);
diff --git a/src/qreg.h b/src/qreg.h
index ca35e78..1eb2cf8 100644
--- a/src/qreg.h
+++ b/src/qreg.h
@@ -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);
diff --git a/src/ring.c b/src/ring.c
index dc43ac5..d7643d0 100644
--- a/src/ring.c
+++ b/src/ring.c
@@ -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;
diff --git a/src/ring.h b/src/ring.h
index 126f455..f5606d1 100644
--- a/src/ring.h
+++ b/src/ring.h
@@ -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;
diff --git a/src/view.c b/src/view.c
index 43cdd3f..d866a5e 100644
--- a/src/view.c
+++ b/src/view.c
@@ -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)
{
diff --git a/src/view.h b/src/view.h
index 887642a..e7c6fa7 100644
--- a/src/view.h
+++ b/src/view.h
@@ -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])