diff options
-rw-r--r-- | TODO | 2 | ||||
-rw-r--r-- | src/qreg-commands.c | 4 | ||||
-rw-r--r-- | src/qreg.c | 186 | ||||
-rw-r--r-- | src/qreg.h | 14 | ||||
-rw-r--r-- | src/ring.c | 2 | ||||
-rw-r--r-- | tests/testsuite.at | 16 |
6 files changed, 156 insertions, 68 deletions
@@ -4,8 +4,6 @@ Tasks: "edit" hook. Known Bugs: - * E%$...$ broken. - Similarily EQ$file$ won't do what you would expect it to. * <23(1;)> leaves values on the stack and the internal hidden brace open.. Apparently breaking from within expressions is not currently safe. diff --git a/src/qreg-commands.c b/src/qreg-commands.c index a96eb5f..41e4465 100644 --- a/src/qreg-commands.c +++ b/src/qreg-commands.c @@ -149,7 +149,7 @@ teco_state_loadqreg_done(teco_machine_main_t *ctx, const teco_string_t *str, GEr if (str->len > 0) { /* Load file into Q-Register */ g_autofree gchar *filename = teco_file_expand_path(str->data); - if (!teco_qreg_load(qreg, filename, error)) + if (!qreg->vtable->load(qreg, filename, error)) return NULL; } else { /* Edit Q-Register */ @@ -202,7 +202,7 @@ teco_state_saveqreg_done(teco_machine_main_t *ctx, const teco_string_t *str, GEr return &teco_state_start; g_autofree gchar *filename = teco_file_expand_path(str->data); - return teco_qreg_save(qreg, filename, error) ? &teco_state_start : NULL; + return qreg->vtable->save(qreg, filename, error) ? &teco_state_start : NULL; } /*$ E% E%q @@ -133,56 +133,6 @@ teco_qreg_set_eol_mode(teco_qreg_t *qreg, gint mode) teco_doc_edit(&teco_qreg_current->string, 0); } -/** @memberof teco_qreg_t */ -gboolean -teco_qreg_load(teco_qreg_t *qreg, const gchar *filename, GError **error) -{ - if (!qreg->vtable->undo_set_string(qreg, error)) - return FALSE; - - if (teco_qreg_current) - teco_doc_update(&teco_qreg_current->string, teco_qreg_view); - - teco_doc_edit(&qreg->string, teco_default_codepage()); - teco_doc_reset(&qreg->string); - - /* - * teco_view_load() might change the EOL style. - */ - teco_qreg_undo_set_eol_mode(qreg); - - /* - * undo_set_string() pushes undo tokens that restore - * the previous document in the view. - * So if loading fails, teco_qreg_current will be - * made the current document again. - */ - if (!teco_view_load(teco_qreg_view, filename, error)) - return FALSE; - - if (teco_qreg_current) - teco_doc_edit(&teco_qreg_current->string, 0); - - return TRUE; -} - -/** @memberof teco_qreg_t */ -gboolean -teco_qreg_save(teco_qreg_t *qreg, const gchar *filename, GError **error) -{ - if (teco_qreg_current) - teco_doc_update(&teco_qreg_current->string, teco_qreg_view); - - teco_doc_edit(&qreg->string, teco_default_codepage()); - - gboolean ret = teco_view_save(teco_qreg_view, filename, error); - - if (teco_qreg_current) - teco_doc_edit(&teco_qreg_current->string, 0); - - return ret; -} - static gboolean teco_qreg_plain_set_integer(teco_qreg_t *qreg, teco_int_t value, GError **error) { @@ -343,6 +293,54 @@ teco_qreg_plain_undo_edit(teco_qreg_t *qreg, GError **error) return TRUE; } +static gboolean +teco_qreg_plain_load(teco_qreg_t *qreg, const gchar *filename, GError **error) +{ + if (!qreg->vtable->undo_set_string(qreg, error)) + return FALSE; + + if (teco_qreg_current) + teco_doc_update(&teco_qreg_current->string, teco_qreg_view); + + teco_doc_edit(&qreg->string, teco_default_codepage()); + teco_doc_reset(&qreg->string); + + /* + * teco_view_load() might change the EOL style. + */ + teco_qreg_undo_set_eol_mode(qreg); + + /* + * undo_set_string() pushes undo tokens that restore + * the previous document in the view. + * So if loading fails, teco_qreg_current will be + * made the current document again. + */ + if (!teco_view_load(teco_qreg_view, filename, error)) + return FALSE; + + if (teco_qreg_current) + teco_doc_edit(&teco_qreg_current->string, 0); + + return TRUE; +} + +static gboolean +teco_qreg_plain_save(teco_qreg_t *qreg, const gchar *filename, GError **error) +{ + if (teco_qreg_current) + teco_doc_update(&teco_qreg_current->string, teco_qreg_view); + + teco_doc_edit(&qreg->string, teco_default_codepage()); + + gboolean ret = teco_view_save(teco_qreg_view, filename, error); + + if (teco_qreg_current) + teco_doc_edit(&teco_qreg_current->string, 0); + + return ret; +} + /** * Initializer for vtables of Q-Registers with "plain" storage of strings. * These store their string part as teco_docs. @@ -362,6 +360,8 @@ teco_qreg_plain_undo_edit(teco_qreg_t *qreg, GError **error) .undo_exchange_string = teco_qreg_plain_undo_exchange_string, \ .edit = teco_qreg_plain_edit, \ .undo_edit = teco_qreg_plain_undo_edit, \ + .load = teco_qreg_plain_load, \ + .save = teco_qreg_plain_save, \ ##__VA_ARGS__ \ } @@ -454,6 +454,55 @@ teco_qreg_external_get_length(teco_qreg_t *qreg, GError **error) return g_utf8_strlen(str.data, str.len); } +/* + * NOTE: This does not perform EOL normalization unlike teco_view_load(). + * It shouldn't be critical since "external" registers are mainly used for filenames. + * Otherwise we could of course load into the view() and call set_string() afterwards. + */ +static gboolean +teco_qreg_external_load(teco_qreg_t *qreg, const gchar *filename, GError **error) +{ + g_auto(teco_string_t) str = {NULL, 0}; + + return g_file_get_contents(filename, &str.data, &str.len, error) && + qreg->vtable->undo_set_string(qreg, error) && + qreg->vtable->set_string(qreg, str.data, str.len, teco_default_codepage(), error); +} + +/* + * NOTE: This does not simply use g_file_set_contents(), as we have to create + * save point files as well. + * FIXME: On the other hand, this does not set the correct EOL style on the document, + * so teco_view_save() will save only with the default EOL style. + * It might therefore still be a good idea to avoid any conversion. + */ +static gboolean +teco_qreg_external_save(teco_qreg_t *qreg, const gchar *filename, GError **error) +{ + if (teco_qreg_current) + teco_doc_update(&teco_qreg_current->string, teco_qreg_view); + + teco_doc_edit(&qreg->string, teco_default_codepage()); + + g_auto(teco_string_t) str = {NULL, 0}; + 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_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); + + if (teco_qreg_current) + teco_doc_edit(&teco_qreg_current->string, 0); + + return ret; +} + /** * Initializer for vtables of Q-Registers with "external" storage of strings. * These rely on custom implementations of get_string() and set_string(). @@ -464,6 +513,8 @@ teco_qreg_external_get_length(teco_qreg_t *qreg, GError **error) .edit = teco_qreg_external_edit, \ .get_character = teco_qreg_external_get_character, \ .get_length = teco_qreg_external_get_length, \ + .load = teco_qreg_external_load, \ + .save = teco_qreg_external_save, \ ##__VA_ARGS__ \ ) @@ -549,7 +600,7 @@ teco_qreg_bufferinfo_get_string(teco_qreg_t *qreg, gchar **str, gsize *len, teco_qreg_t * teco_qreg_bufferinfo_new(void) { - static teco_qreg_vtable_t vtable = TECO_INIT_QREG( + static teco_qreg_vtable_t vtable = TECO_INIT_QREG_EXTERNAL( .set_integer = teco_qreg_bufferinfo_set_integer, .undo_set_integer = teco_qreg_bufferinfo_undo_set_integer, .get_integer = teco_qreg_bufferinfo_get_integer, @@ -558,10 +609,14 @@ teco_qreg_bufferinfo_new(void) .append_string = teco_qreg_bufferinfo_append_string, .undo_append_string = teco_qreg_bufferinfo_undo_append_string, .get_string = teco_qreg_bufferinfo_get_string, - /* we don't want to inherit all the other stuff from TECO_INIT_QREG_EXTERNAL(). */ - .edit = teco_qreg_external_edit, - .get_character = teco_qreg_external_get_character, - .get_length = teco_qreg_external_get_length + /* + * As teco_qreg_bufferinfo_set_string() is not implemented, + * it's important to not inherit teco_qreg_external_exchange_string(). + * `[*` and `]*` will still work though. + * The inherited teco_qreg_external_load() will simply fail. + */ + .exchange_string = teco_qreg_plain_exchange_string, + .undo_exchange_string = teco_qreg_plain_undo_exchange_string ); return teco_qreg_new(&vtable, "*", 1); @@ -795,6 +850,24 @@ teco_qreg_clipboard_get_string(teco_qreg_t *qreg, gchar **str, gsize *len, return TRUE; } +/* + * Regardless of whether EOL normalization is enabled, + * this will never perform it. + * Other than that, it's very similar to teco_qreg_external_load(). + */ +static gboolean +teco_qreg_clipboard_load(teco_qreg_t *qreg, const gchar *filename, GError **error) +{ + g_assert(!teco_string_contains(&qreg->head.name, '\0')); + const gchar *clipboard_name = qreg->head.name.data + 1; + + g_auto(teco_string_t) str = {NULL, 0}; + + return g_file_get_contents(filename, &str.data, &str.len, error) && + teco_qreg_clipboard_undo_set_string(qreg, error) && + teco_interface_set_clipboard(clipboard_name, str.data, str.len, error); +} + /** @static @memberof teco_qreg_t */ teco_qreg_t * teco_qreg_clipboard_new(const gchar *name) @@ -804,7 +877,8 @@ teco_qreg_clipboard_new(const gchar *name) .undo_set_string = teco_qreg_clipboard_undo_set_string, .append_string = teco_qreg_clipboard_append_string, .undo_append_string = teco_qreg_clipboard_undo_append_string, - .get_string = teco_qreg_clipboard_get_string + .get_string = teco_qreg_clipboard_get_string, + .load = teco_qreg_clipboard_load ); teco_qreg_t *qreg = teco_qreg_new(&vtable, "~", 1); @@ -72,6 +72,13 @@ typedef const struct { gboolean (*edit)(teco_qreg_t *qreg, GError **error); gboolean (*undo_edit)(teco_qreg_t *qreg, GError **error); + + /* + * Load and save already care about undo token + * creation. + */ + gboolean (*load)(teco_qreg_t *qreg, const gchar *filename, GError **error); + gboolean (*save)(teco_qreg_t *qreg, const gchar *filename, GError **error); } teco_qreg_vtable_t; /** @extends teco_rb3str_head_t */ @@ -113,13 +120,6 @@ gboolean teco_qreg_execute(teco_qreg_t *qreg, teco_qreg_table_t *qreg_table_loca void teco_qreg_undo_set_eol_mode(teco_qreg_t *qreg); void teco_qreg_set_eol_mode(teco_qreg_t *qreg, gint mode); -/* - * Load and save already care about undo token - * creation. - */ -gboolean teco_qreg_load(teco_qreg_t *qreg, const gchar *filename, GError **error); -gboolean teco_qreg_save(teco_qreg_t *qreg, const gchar *filename, GError **error); - /** @memberof teco_qreg_t */ static inline void teco_qreg_free(teco_qreg_t *qreg) @@ -524,7 +524,7 @@ teco_state_save_file_done(teco_machine_main_t *ctx, const teco_string_t *str, GE g_autofree gchar *filename = teco_file_expand_path(str->data); if (teco_qreg_current) { - if (!teco_qreg_save(teco_qreg_current, filename, error)) + if (!teco_qreg_current->vtable->save(teco_qreg_current, filename, error)) return NULL; } else { if (!teco_buffer_save(teco_ring_current, *filename ? filename : NULL, error)) diff --git a/tests/testsuite.at b/tests/testsuite.at index 648e76c..fa14140 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -84,6 +84,22 @@ AT_CHECK([$SCITECO -e '@^Ua{@EQ.x//} :Ma @^U.x/FOO/'], 0, ignore, ignore) AT_CHECK([$SCITECO -e '@^Ua{@EQ.x//} Ma @^U.x/FOO/'], 1, ignore, ignore) AT_CLEANUP +AT_SETUP([Loading files into Q-Registers]) +AT_CHECK([echo -n ".." >loadqreg.txt], 0, ignore, ignore) +AT_CHECK([$SCITECO -e "@EQa/loadqreg.txt/ :Qa-2\"N(0/0)'"], 0, ignore, ignore) +# Does the same as FG..$. Afterwards, the parent directory should be shorter. +AT_CHECK([$SCITECO -e ":Q\$Ul @EQ\$/loadqreg.txt/ :Q\$-Ql+1\">(0/0)'"], 0, ignore, ignore) +AT_CLEANUP + +AT_SETUP([Saving Q-Registers contents to files]) +AT_CHECK([$SCITECO -e '@^Ua/test/ @E%a/saveqreg.txt/'], 0, ignore, ignore) +AT_CHECK([test "`cat saveqreg.txt`" = test], 0, ignore, ignore) +# The result of E%$ should always have forward slashes, +# so this check will work on Win32 as well. +AT_CHECK([$SCITECO -e '@E%$/saveqreg.txt/'], 0, ignore, ignore) +AT_CHECK([test "`cat saveqreg.txt`" = "$PWD"], 0, ignore, ignore) +AT_CLEANUP + AT_SETUP([8-bit cleanliness]) AT_CHECK([$SCITECO -e "0@I//J 0A\"N(0/0)' :@S/^@/\"F(0/0)'"], 0, ignore, ignore) AT_CHECK([$SCITECO -e "@EQa//0EE 1U*0EE 0:@EUa/f^@^@/ :Qa-4\"N(0/0)' Ga Z-4\"N(0/0)'"], 0, ignore, ignore) |