diff options
-rw-r--r-- | TODO | 4 | ||||
-rw-r--r-- | src/core-commands.c | 2 | ||||
-rw-r--r-- | src/qreg.c | 2 | ||||
-rw-r--r-- | src/ring.c | 36 | ||||
-rw-r--r-- | src/ring.h | 1 | ||||
-rw-r--r-- | src/view.c | 62 | ||||
-rw-r--r-- | src/view.h | 13 | ||||
-rw-r--r-- | tests/testsuite.at | 6 |
8 files changed, 96 insertions, 30 deletions
@@ -318,8 +318,6 @@ Features: rubbed out command line). * some missing useful VideoTECO/TECO-11 commands and unnecessary incompatibilities: - * ER command: read file(s) into current buffer at dot. - Video TECO accepts a glob pattern here. * nEW to save a buffer by id * EI$ would "resume" command input from the terminal in TECO-11. But how is that different from ^C/$$ in SciTECO? @@ -359,7 +357,7 @@ Features: when detecting interactive invocation. But perhaps there should rather be a global configurable delay/refresh that could be considered by all loops etc. - * Video TECO: EQq...$ accepts glob patterns and reads + * Video TECO: EQq...$ and ER...$ accepts glob patterns and reads all files into the register. But we probably won't want to have that. * :EB -> Bool on Video TECO. * Video TECO: EV as an alias for EB, but create read-only buffer. diff --git a/src/core-commands.c b/src/core-commands.c index 0ff2b81..8e31ae3 100644 --- a/src/core-commands.c +++ b/src/core-commands.c @@ -2686,6 +2686,8 @@ teco_state_ecommand_input(teco_machine_main_t *ctx, gunichar chr, GError **error .modifier_at = TRUE, .modifier_colon = 1}, ['W'] = {&teco_state_save_file, .modifier_at = TRUE}, + ['R'] = {&teco_state_read_file, + .modifier_at = TRUE}, /* * Commands @@ -337,7 +337,7 @@ teco_qreg_plain_load(teco_qreg_t *qreg, const gchar *filename, GError **error) * So if loading fails, teco_qreg_current will be * made the current document again. */ - if (!teco_view_load(teco_qreg_view, filename, error)) + if (!teco_view_load(teco_qreg_view, filename, TRUE, error)) return FALSE; if (teco_qreg_current) @@ -77,7 +77,7 @@ teco_buffer_undo_edit(teco_buffer_t *ctx) static gboolean teco_buffer_load(teco_buffer_t *ctx, const gchar *filename, GError **error) { - if (!teco_view_load(ctx->view, filename, error)) + if (!teco_view_load(ctx->view, filename, TRUE, error)) return FALSE; #if 0 /* NOTE: currently buffer cannot be dirty */ @@ -595,6 +595,40 @@ teco_state_save_file_done(teco_machine_main_t *ctx, const teco_string_t *str, GE */ TECO_DEFINE_STATE_EXPECTFILE(teco_state_save_file); +static teco_state_t * +teco_state_read_file_done(teco_machine_main_t *ctx, const teco_string_t *str, GError **error) +{ + if (ctx->flags.mode > TECO_MODE_NORMAL) + return &teco_state_start; + + sptr_t pos = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0); + + g_autofree gchar *filename = teco_file_expand_path(str->data); + /* FIXME: Add wrapper to interface.h? */ + if (!teco_view_load(teco_interface_current_view, filename, FALSE, error)) + return NULL; + + if (teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0) != pos) { + teco_ring_dirtify(); + + if (teco_current_doc_must_undo()) + undo__teco_interface_ssm(SCI_UNDO, 0, 0); + } + + return &teco_state_start; +} + +/*$ ER read + * ER<file>$ -- Read and insert file into current buffer + * + * Reads and inserts the given <file> into the current buffer or Q-Register at dot. + * Dot is left immediately after the given file. + */ +/* + * NOTE: Video TECO allows glob patterns as an argument. + */ +TECO_DEFINE_STATE_EXPECTFILE(teco_state_read_file); + /*$ EF close * [n]EF -- Remove buffer from ring * -EF @@ -99,6 +99,7 @@ void teco_ring_cleanup(void); TECO_DECLARE_STATE(teco_state_edit_file); TECO_DECLARE_STATE(teco_state_save_file); +TECO_DECLARE_STATE(teco_state_read_file); void teco_state_ecommand_close(teco_machine_main_t *ctx, GError **error); @@ -198,16 +198,22 @@ teco_view_set_representations(teco_view_t *ctx) * * @param ctx The view to load. * @param channel Channel to read from. + * @param clear Whether to completely replace document + * (leaving dot at the beginning of the document) or insert at dot + * (leaving dot at the end of the insertion). * @param error A GError. * @return FALSE in case of a GError. * * @memberof teco_view_t */ gboolean -teco_view_load_from_channel(teco_view_t *ctx, GIOChannel *channel, GError **error) +teco_view_load_from_channel(teco_view_t *ctx, GIOChannel *channel, + gboolean clear, GError **error) { gboolean ret = TRUE; + unsigned int message = SCI_ADDTEXT; + g_auto(teco_eol_reader_t) reader; teco_eol_reader_init_gio(&reader, channel); @@ -225,22 +231,27 @@ teco_view_load_from_channel(teco_view_t *ctx, GIOChannel *channel, GError **erro SC_LINECHARACTERINDEX_UTF32, 0); teco_view_ssm(ctx, SCI_BEGINUNDOACTION, 0, 0); - teco_view_ssm(ctx, SCI_CLEARALL, 0, 0); + if (clear) { + teco_view_ssm(ctx, SCI_CLEARALL, 0, 0); - /* - * Preallocate memory based on the file size. - * May waste a few bytes if file contains DOS EOLs - * and EOL translation is enabled, but is faster. - * NOTE: g_io_channel_unix_get_fd() should report the correct fd - * on Windows, too. - */ - struct stat stat_buf = {.st_size = 0}; - if (!fstat(g_io_channel_unix_get_fd(channel), &stat_buf) && - stat_buf.st_size > 0) { - ret = teco_memory_check(stat_buf.st_size, error); - if (!ret) - goto cleanup; - teco_view_ssm(ctx, SCI_ALLOCATE, stat_buf.st_size, 0); + /* + * Preallocate memory based on the file size. + * May waste a few bytes if file contains DOS EOLs + * and EOL translation is enabled, but is faster. + * NOTE: g_io_channel_unix_get_fd() should report the correct fd + * on Windows, too. + */ + struct stat stat_buf = {.st_size = 0}; + if (!fstat(g_io_channel_unix_get_fd(channel), &stat_buf) && + stat_buf.st_size > 0) { + ret = teco_memory_check(stat_buf.st_size, error); + if (!ret) + goto cleanup; + teco_view_ssm(ctx, SCI_ALLOCATE, stat_buf.st_size, 0); + } + + /* keep dot at beginning of document */ + message = SCI_APPENDTEXT; } for (;;) { @@ -258,7 +269,7 @@ teco_view_load_from_channel(teco_view_t *ctx, GIOChannel *channel, GError **erro if (rc == G_IO_STATUS_EOF) break; - teco_view_ssm(ctx, SCI_APPENDTEXT, str.len, (sptr_t)str.data); + teco_view_ssm(ctx, message, str.len, (sptr_t)str.data); /* * Even if we checked initially, knowing the file size, @@ -285,7 +296,7 @@ teco_view_load_from_channel(teco_view_t *ctx, GIOChannel *channel, GError **erro * If it is enabled but the stream does not contain any * EOL characters, the platform default is still assumed. */ - if (reader.eol_style >= 0) + if (clear && reader.eol_style >= 0) teco_view_ssm(ctx, SCI_SETEOLMODE, reader.eol_style, 0); if (reader.eol_style_inconsistent) @@ -303,12 +314,21 @@ cleanup: } /** - * Load view's document from file. + * Load file into view's document. + * + * @param ctx The view to load. + * @param filename File name to read + * @param clear Whether to completely replace document + * (leaving dot at the beginning of the document) or insert at dot + * (leaving dot at the end of the insertion). + * @param error A GError. + * @return FALSE in case of a GError. * * @memberof teco_view_t */ gboolean -teco_view_load_from_file(teco_view_t *ctx, const gchar *filename, GError **error) +teco_view_load_from_file(teco_view_t *ctx, const gchar *filename, + gboolean clear, GError **error) { g_autoptr(GIOChannel) channel = g_io_channel_new_file(filename, "r", error); if (!channel) @@ -322,7 +342,7 @@ teco_view_load_from_file(teco_view_t *ctx, const gchar *filename, GError **error g_io_channel_set_encoding(channel, NULL, NULL); g_io_channel_set_buffered(channel, FALSE); - if (!teco_view_load_from_channel(ctx, channel, error)) { + if (!teco_view_load_from_channel(ctx, channel, clear, error)) { g_prefix_error(error, "Error reading file \"%s\": ", filename); return FALSE; } @@ -52,13 +52,17 @@ teco_view_set_scintilla_undo(teco_view_t *ctx, gboolean state) teco_view_ssm(ctx, SCI_SETUNDOCOLLECTION, state, 0); } -gboolean teco_view_load_from_channel(teco_view_t *ctx, GIOChannel *channel, GError **error); -gboolean teco_view_load_from_file(teco_view_t *ctx, const gchar *filename, GError **error); +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, + gboolean clear, GError **error); /** @memberof teco_view_t */ -#define teco_view_load(CTX, FROM, ERROR) \ +#define teco_view_load(CTX, FROM, CLEAR, ERROR) \ (_Generic((FROM), GIOChannel * : teco_view_load_from_channel, \ - const gchar * : teco_view_load_from_file)((CTX), (FROM), (ERROR))) + gchar * : teco_view_load_from_file, \ + const gchar * : teco_view_load_from_file)((CTX), (FROM), \ + (CLEAR), (ERROR))) gboolean teco_view_save_to_channel(teco_view_t *ctx, GIOChannel *channel, GError **error); gboolean teco_view_save_to_file(teco_view_t *ctx, const gchar *filename, GError **error); @@ -66,6 +70,7 @@ gboolean teco_view_save_to_file(teco_view_t *ctx, const gchar *filename, GError /** @memberof teco_view_t */ #define teco_view_save(CTX, TO, ERROR) \ (_Generic((TO), GIOChannel * : teco_view_save_to_channel, \ + gchar * : teco_view_save_to_file, \ const gchar * : teco_view_save_to_file)((CTX), (TO), (ERROR))) /** @pure @memberof teco_view_t */ diff --git a/tests/testsuite.at b/tests/testsuite.at index b0db94d..8d4daa7 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -245,6 +245,12 @@ TE_CHECK([[@EB/foo/ @I/XXX/ :EF :Q*"N(0/0)' @EB/foo/ Z-3"N(0/0)']], 0, ignore, i TE_CHECK([[@EB/foo/ 1EF :Q*"=(0/0)']], 0, ignore, ignore) AT_CLEANUP +AT_SETUP([Read file into current buffer]) +AT_DATA([test.txt], [[0123456789 +]]) +TE_CHECK([[@I/Helloworld/5R @ER"test.txt" .-5-11"N(0/0)']], 0, ignore, ignore) +AT_CLEANUP + AT_SETUP([8-bit cleanliness]) TE_CHECK([[0@I//J 0A"N(0/0)' :@S/^@/"F(0/0)']], 0, ignore, ignore) TE_CHECK([[@EQa//0EE 1U*0EE 0:@EUa/f^@^@/ :Qa-4"N(0/0)' Ga Z-4"N(0/0)']], 0, ignore, ignore) |