From e371b5fc710c597e175e6b7d7645b3a224a731a1 Mon Sep 17 00:00:00 2001 From: Robin Haberkorn Date: Fri, 25 Sep 2026 01:43:56 +0200 Subject: instead of relying on Scintilla undo actions, use SciTECO undo tokens exclusively Previously we were using both Scintilla undo actions and our own undo tokens and both had to be coordinated via `undo__teco_interface_ssm(SCI_UNDO, 0, 0)`. This was error prone since we have to predict when an undo action is actually generated. Furthermore, you had to update SCI_SETUNDOCOLLECTION on all buffers and the Q-Reg view whenever switching to and from interactive mode. Also, this probably wasted memory for the doubled undo bookkeeping. Now, we always set SCI_SETUNDOCOLLECTION(FALSE), even in interactive mode. teco_undo_view_insert() was introduced to undo text deletions. While this saves almost no lines of code, it's just a clearer and simpler architecture. --- src/cmdline.c | 3 --- src/core-commands.c | 64 +++++++++++++++++++++++++---------------------------- src/glob.c | 17 ++++++-------- src/interface.h | 6 +++++ src/main.c | 5 ----- src/move-commands.c | 21 +++++------------- src/qreg-commands.c | 15 +++---------- src/qreg.c | 19 ++++++++-------- src/ring.c | 11 +-------- src/ring.h | 2 -- src/search.c | 27 ++++++++-------------- src/spawn.c | 12 +++++----- src/view.c | 58 +++++++++++++++++++++++++++++++++++++++++++----- src/view.h | 10 ++------- 14 files changed, 131 insertions(+), 139 deletions(-) diff --git a/src/cmdline.c b/src/cmdline.c index d2d9d04..99bc802 100644 --- a/src/cmdline.c +++ b/src/cmdline.c @@ -382,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 612d980..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); @@ -566,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, @@ -2736,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) { @@ -2985,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--) { @@ -2994,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 */ @@ -3010,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++) @@ -3038,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; } @@ -3100,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 */ @@ -3115,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/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/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/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/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/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/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 2b2a696..31a551e 100644 --- a/src/search.c +++ b/src/search.c @@ -1353,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); @@ -1361,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; @@ -1384,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; @@ -1438,13 +1429,13 @@ teco_state_search_delete_done(teco_machine_main_t *ctx, teco_string_t str, GErro 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; diff --git a/src/spawn.c b/src/spawn.c index a4bf381..933d8f4 100644 --- a/src/spawn.c +++ b/src/spawn.c @@ -422,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); @@ -437,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) { @@ -446,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/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); -- cgit v1.2.3