aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/ring.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ring.c')
-rw-r--r--src/ring.c431
1 files changed, 314 insertions, 117 deletions
diff --git a/src/ring.c b/src/ring.c
index b7a75d1..2445718 100644
--- a/src/ring.c
+++ b/src/ring.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2025 Robin Haberkorn
+ * Copyright (C) 2012-2026 Robin Haberkorn
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@
#include <glib.h>
#include <glib/gprintf.h>
+#include <glib/gstdio.h>
#include <Scintilla.h>
@@ -55,7 +56,8 @@ teco_buffer_set_filename(teco_buffer_t *ctx, const gchar *filename)
gchar *resolved = teco_file_get_absolute_path(filename);
g_free(ctx->filename);
ctx->filename = resolved;
- teco_interface_info_update(ctx);
+ if (ctx == teco_ring_current && !teco_qreg_current)
+ teco_interface_info_update(ctx);
}
/** @memberof teco_buffer_t */
@@ -74,16 +76,23 @@ teco_buffer_undo_edit(teco_buffer_t *ctx)
}
/** @private @memberof teco_buffer_t */
+static inline gchar *
+teco_buffer_get_recovery(teco_buffer_t *ctx)
+{
+ g_autofree gchar *dirname = g_path_get_dirname(ctx->filename);
+ g_autofree gchar *basename = g_path_get_basename(ctx->filename);
+ return g_strconcat(dirname, G_DIR_SEPARATOR_S, "#", basename, "#", NULL);
+}
+
+/** @private @memberof teco_buffer_t */
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 */
- undo__teco_interface_info_update_buffer(ctx);
- teco_undo_gboolean(ctx->dirty) = FALSE;
-#endif
+ /* currently buffer cannot be dirty */
+ g_assert(ctx->state == TECO_BUFFER_CLEAN);
teco_buffer_set_filename(ctx, filename);
return TRUE;
@@ -107,8 +116,15 @@ teco_buffer_save(teco_buffer_t *ctx, const gchar *filename, GError **error)
* Undirtify
* NOTE: info update is performed by set_filename()
*/
- undo__teco_interface_info_update_buffer(ctx);
- teco_undo_gboolean(ctx->dirty) = FALSE;
+ if (ctx == teco_ring_current && !teco_qreg_current)
+ undo__teco_interface_info_update_buffer(ctx);
+ if (ctx->state > TECO_BUFFER_DIRTY_NO_DUMP) {
+ g_autofree gchar *filename_recovery = teco_buffer_get_recovery(ctx);
+ g_unlink(filename_recovery);
+ /* on rubout, we do not restore the recovery file */
+ ctx->state = TECO_BUFFER_DIRTY_NO_DUMP;
+ }
+ teco_undo_guint(ctx->state) = TECO_BUFFER_CLEAN;
/*
* FIXME: necessary also if the filename was not specified but the file
@@ -127,6 +143,11 @@ teco_buffer_save(teco_buffer_t *ctx, const gchar *filename, GError **error)
static inline void
teco_buffer_free(teco_buffer_t *ctx)
{
+ if (ctx->state > TECO_BUFFER_DIRTY_NO_DUMP) {
+ g_autofree gchar *filename_recovery = teco_buffer_get_recovery(ctx);
+ g_unlink(filename_recovery);
+ }
+
teco_view_free(ctx->view);
g_free(ctx->filename);
g_free(ctx);
@@ -151,7 +172,7 @@ teco_ring_last(void)
}
static void
-teco_undo_ring_edit_action(teco_buffer_t **buffer, gboolean run)
+teco_undo_ring_reinsert_action(teco_buffer_t **buffer, gboolean run)
{
if (run) {
/*
@@ -162,22 +183,19 @@ teco_undo_ring_edit_action(teco_buffer_t **buffer, gboolean run)
teco_tailq_insert_before((*buffer)->entry.next, &(*buffer)->entry);
else
teco_tailq_insert_tail(&teco_ring_head, &(*buffer)->entry);
-
- teco_ring_current = *buffer;
- teco_buffer_edit(*buffer);
} else {
teco_buffer_free(*buffer);
}
}
-/*
- * Emitted after a buffer close
- * The pointer is the only remaining reference to the buffer!
+/**
+ * Insert buffer during undo (for closing buffers).
+ * Ownership of the buffer is passed to the undo token.
*/
static void
-teco_undo_ring_edit(teco_buffer_t *buffer)
+teco_undo_ring_reinsert(teco_buffer_t *buffer)
{
- teco_buffer_t **ctx = teco_undo_push_size((teco_undo_action_t)teco_undo_ring_edit_action,
+ teco_buffer_t **ctx = teco_undo_push_size((teco_undo_action_t)teco_undo_ring_reinsert_action,
sizeof(buffer));
if (ctx)
*ctx = buffer;
@@ -223,27 +241,57 @@ teco_ring_find_by_id(teco_int_t id)
return NULL;
}
+static void
+teco_ring_undirtify(void)
+{
+ if (teco_ring_current->state > TECO_BUFFER_DIRTY_NO_DUMP) {
+ g_autofree gchar *filename_recovery = teco_buffer_get_recovery(teco_ring_current);
+ g_unlink(filename_recovery);
+ }
+
+ teco_ring_current->state = TECO_BUFFER_CLEAN;
+ teco_interface_info_update(teco_ring_current);
+}
+
+TECO_DEFINE_UNDO_CALL(teco_ring_undirtify);
+
void
teco_ring_dirtify(void)
{
- if (teco_qreg_current || teco_ring_current->dirty)
+ if (teco_qreg_current)
return;
- undo__teco_interface_info_update_buffer(teco_ring_current);
- teco_undo_gboolean(teco_ring_current->dirty) = TRUE;
- teco_interface_info_update(teco_ring_current);
+ switch ((teco_buffer_state_t)teco_ring_current->state) {
+ case TECO_BUFFER_CLEAN:
+ teco_ring_current->state = TECO_BUFFER_DIRTY_NO_DUMP;
+ teco_interface_info_update(teco_ring_current);
+ undo__teco_ring_undirtify();
+ break;
+ case TECO_BUFFER_DIRTY_NO_DUMP:
+ case TECO_BUFFER_DIRTY_OUTDATED_DUMP:
+ break;
+ case TECO_BUFFER_DIRTY_RECENT_DUMP:
+ teco_ring_current->state = TECO_BUFFER_DIRTY_OUTDATED_DUMP;
+ /* set to TECO_BUFFER_DIRTY_OUTDATED_DUMP on rubout */
+ teco_undo_guint(teco_ring_current->state);
+ break;
+ }
}
-gboolean
-teco_ring_is_any_dirty(void)
+/** Get id of first dirty buffer, or otherwise 0 */
+guint
+teco_ring_get_first_dirty(void)
{
+ guint id = 1;
+
for (teco_tailq_entry_t *cur = teco_ring_head.first; cur != NULL; cur = cur->next) {
teco_buffer_t *buffer = (teco_buffer_t *)cur;
- if (buffer->dirty)
- return TRUE;
+ if (buffer->state > TECO_BUFFER_CLEAN)
+ return id;
+ id++;
}
- return FALSE;
+ return 0;
}
gboolean
@@ -252,13 +300,72 @@ teco_ring_save_all_dirty_buffers(GError **error)
for (teco_tailq_entry_t *cur = teco_ring_head.first; cur != NULL; cur = cur->next) {
teco_buffer_t *buffer = (teco_buffer_t *)cur;
/* NOTE: Will fail for a dirty unnamed file */
- if (buffer->dirty && !teco_buffer_save(buffer, NULL, error))
+ if (buffer->state > TECO_BUFFER_CLEAN &&
+ !teco_buffer_save(buffer, NULL, error))
return FALSE;
}
return TRUE;
}
+/**
+ * Recovery creation interval in seconds or 0 if disabled.
+ * It's not currently enforced in batch mode.
+ */
+guint teco_ring_recovery_interval = 5*60;
+
+/**
+ * Create recovery files for all dirty buffers.
+ *
+ * Should be called by the interface every teco_ring_recovery_interval seconds.
+ * This does not generate or expect undo tokens, so it can be called
+ * even when idlying.
+ */
+void
+teco_ring_dump_recovery(void)
+{
+ g_assert(teco_ring_recovery_interval > 0);
+
+ for (teco_tailq_entry_t *cur = teco_ring_head.first; cur != NULL; cur = cur->next) {
+ teco_buffer_t *buffer = (teco_buffer_t *)cur;
+ /* already dumped buffers don't have to be written again */
+ if (buffer->state != TECO_BUFFER_DIRTY_NO_DUMP &&
+ buffer->state != TECO_BUFFER_DIRTY_OUTDATED_DUMP)
+ continue;
+
+ /*
+ * Dirty unnamed buffers cannot be backed up.
+ * FIXME: Perhaps they should be dumped under ~/#UNNAMED#?
+ */
+ if (!buffer->filename)
+ continue;
+
+ g_autofree gchar *filename_recovery = teco_buffer_get_recovery(buffer);
+
+ g_autoptr(GIOChannel) channel = g_io_channel_new_file(filename_recovery, "w", NULL);
+ if (!channel)
+ continue;
+
+ /*
+ * teco_view_save_to_channel() expects a buffered and blocking channel.
+ */
+ g_io_channel_set_encoding(channel, NULL, NULL);
+ g_io_channel_set_buffered(channel, TRUE);
+
+ /*
+ * This does not use teco_view_save_to_file() since we must not
+ * emit undo tokens.
+ *
+ * FIXME: Errors are silently ignored.
+ * Should we log warnings instead?
+ */
+ if (!teco_view_save_to_channel(buffer->view, channel, NULL))
+ continue;
+
+ buffer->state = TECO_BUFFER_DIRTY_RECENT_DUMP;
+ }
+}
+
gboolean
teco_ring_edit_by_name(const gchar *filename, GError **error)
{
@@ -306,8 +413,7 @@ teco_ring_edit_by_id(teco_int_t id, GError **error)
{
teco_buffer_t *buffer = teco_ring_find(id);
if (!buffer) {
- g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED,
- "Invalid buffer id %" TECO_INT_FORMAT, id);
+ teco_error_invalidbuf_set(error, id);
return FALSE;
}
@@ -320,7 +426,7 @@ teco_ring_edit_by_id(teco_int_t id, GError **error)
}
static void
-teco_ring_close_buffer(teco_buffer_t *buffer)
+teco_ring_remove_buffer(teco_buffer_t *buffer)
{
teco_tailq_remove(&teco_ring_head, &buffer->entry);
@@ -333,32 +439,48 @@ teco_ring_close_buffer(teco_buffer_t *buffer)
"Removed unnamed file from the ring.");
}
-TECO_DEFINE_UNDO_CALL(teco_ring_close_buffer, teco_buffer_t *);
+TECO_DEFINE_UNDO_CALL(teco_ring_remove_buffer, teco_buffer_t *);
+/**
+ * Close the given buffer.
+ * Executes close hooks and changes the current buffer if necessary.
+ * It already pushes undo tokens.
+ */
gboolean
-teco_ring_close(GError **error)
+teco_ring_close(teco_buffer_t *buffer, GError **error)
{
- teco_buffer_t *buffer = teco_ring_current;
+ if (buffer == teco_ring_current) {
+ if (!teco_ed_hook(TECO_ED_HOOK_CLOSE, error))
+ return FALSE;
- if (!teco_ed_hook(TECO_ED_HOOK_CLOSE, error))
- return FALSE;
- teco_ring_close_buffer(buffer);
- teco_ring_current = teco_buffer_next(buffer) ? : teco_buffer_prev(buffer);
- /* Transfer responsibility to the undo token object. */
- teco_undo_ring_edit(buffer);
+ teco_ring_undo_edit();
+ teco_ring_remove_buffer(buffer);
+
+ teco_ring_current = teco_buffer_next(buffer) ? : teco_buffer_prev(buffer);
+ if (!teco_ring_current) {
+ /* edit new unnamed buffer */
+ if (!teco_ring_edit_by_name(NULL, error))
+ return FALSE;
+ } else {
+ teco_buffer_edit(teco_ring_current);
+ if (!teco_ed_hook(TECO_ED_HOOK_EDIT, error))
+ return FALSE;
+ }
+ } else {
+ teco_ring_remove_buffer(buffer);
+ }
- if (!teco_ring_current)
- return teco_ring_edit_by_name(NULL, error);
+ /* transfer responsibility to the undo token object */
+ teco_undo_ring_reinsert(buffer);
- teco_buffer_edit(teco_ring_current);
- return teco_ed_hook(TECO_ED_HOOK_EDIT, error);
+ return TRUE;
}
void
teco_ring_undo_close(void)
{
undo__teco_buffer_free(teco_ring_current);
- undo__teco_ring_close_buffer(teco_ring_current);
+ undo__teco_ring_remove_buffer(teco_ring_current);
}
void
@@ -387,13 +509,6 @@ teco_ring_cleanup(void)
* Command states
*/
-/*
- * FIXME: Should be part of the teco_machine_main_t?
- * Unfortunately, we cannot just merge initial() with done(),
- * since we want to react immediately to xEB without waiting for $.
- */
-static gboolean allow_filename = FALSE;
-
static gboolean
teco_state_edit_file_initial(teco_machine_main_t *ctx, GError **error)
{
@@ -404,18 +519,17 @@ teco_state_edit_file_initial(teco_machine_main_t *ctx, GError **error)
if (!teco_expressions_pop_num_calc(&id, -1, error))
return FALSE;
- allow_filename = TRUE;
+ ctx->flags.allow_filename = TRUE;
if (id == 0) {
- for (teco_buffer_t *cur = teco_ring_first(); cur; cur = teco_buffer_next(cur)) {
- const gchar *filename = cur->filename ? : "(Unnamed)";
- teco_interface_popup_add(TECO_POPUP_FILE, filename,
- strlen(filename), cur == teco_ring_current);
- }
+ for (teco_buffer_t *cur = teco_ring_first(); cur; cur = teco_buffer_next(cur))
+ teco_interface_popup_add(TECO_POPUP_FILE, cur->filename,
+ cur->filename ? strlen(cur->filename) : 0,
+ cur == teco_ring_current);
teco_interface_popup_show(0);
} else if (id > 0) {
- allow_filename = FALSE;
+ ctx->flags.allow_filename = FALSE;
if (!teco_current_doc_undo_edit(error) ||
!teco_ring_edit(id, error))
return FALSE;
@@ -424,24 +538,35 @@ teco_state_edit_file_initial(teco_machine_main_t *ctx, GError **error)
return TRUE;
}
+gboolean
+teco_state_edit_file_process(teco_machine_main_t *ctx, teco_string_t str,
+ gsize new_chars, GError **error)
+{
+ g_assert(new_chars > 0);
+
+ if (!ctx->flags.allow_filename) {
+ g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED,
+ "If a buffer is selected by id, the <EB> "
+ "string argument must be empty");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
static teco_state_t *
-teco_state_edit_file_done(teco_machine_main_t *ctx, const teco_string_t *str, GError **error)
+teco_state_edit_file_done(teco_machine_main_t *ctx, teco_string_t str, GError **error)
{
if (ctx->flags.mode > TECO_MODE_NORMAL)
return &teco_state_start;
- if (!allow_filename) {
- if (str->len > 0) {
- g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED,
- "If a buffer is selected by id, the <EB> "
- "string argument must be empty");
- return NULL;
- }
-
+ if (!ctx->flags.allow_filename) {
+ /* process_cb() already throws error if str.len > 0 */
+ g_assert(str.len == 0);
return &teco_state_start;
}
- g_autofree gchar *filename = teco_file_expand_path(str->data);
+ g_autofree gchar *filename = teco_file_expand_path(str.data);
if (teco_globber_is_pattern(filename)) {
g_auto(teco_globber_t) globber;
teco_globber_init(&globber, filename, G_FILE_TEST_IS_REGULAR);
@@ -465,7 +590,7 @@ teco_state_edit_file_done(teco_machine_main_t *ctx, const teco_string_t *str, GE
/*$ EB edit
* [n]EB[file]$ -- Open or edit file
- * nEB$
+ * [n]EB$
*
* Opens or edits the file with name <file>.
* If <file> is not in the buffer ring it is opened,
@@ -518,45 +643,60 @@ teco_state_edit_file_done(teco_machine_main_t *ctx, const teco_string_t *str, GE
* ecetera.
*/
TECO_DEFINE_STATE_EXPECTGLOB(teco_state_edit_file,
- .initial_cb = (teco_state_initial_cb_t)teco_state_edit_file_initial
+ .initial_cb = (teco_state_initial_cb_t)teco_state_edit_file_initial,
+ .expectstring.process_cb = teco_state_edit_file_process,
+ .expectstring.done_cb = teco_state_edit_file_done
);
static teco_state_t *
-teco_state_save_file_done(teco_machine_main_t *ctx, const teco_string_t *str, GError **error)
+teco_state_save_file_done(teco_machine_main_t *ctx, teco_string_t str, GError **error)
{
if (ctx->flags.mode > TECO_MODE_NORMAL)
return &teco_state_start;
- g_autofree gchar *filename = teco_file_expand_path(str->data);
- if (teco_qreg_current) {
- 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))
+ if (!teco_expressions_eval(FALSE, error))
+ return NULL;
+
+ g_autofree gchar *filename = teco_file_expand_path(str.data);
+
+ /*
+ * This is like implying teco_ring_get_id(teco_ring_current)
+ * but avoids the O(n) ring iterations.
+ */
+ teco_buffer_t *buffer = teco_ring_current;
+ if (teco_expressions_args() > 0) {
+ teco_int_t id = teco_expressions_pop_num(0);
+ buffer = teco_ring_find(id);
+ if (!buffer) {
+ teco_error_invalidbuf_set(error, id);
return NULL;
+ }
+ } else if (teco_qreg_current) {
+ return !teco_qreg_current->vtable->save(teco_qreg_current, filename, error)
+ ? NULL : &teco_state_start;
}
- return &teco_state_start;
+ return !teco_buffer_save(buffer, *filename ? filename : NULL, error) ? NULL : &teco_state_start;
}
/*$ EW write save
- * EW$ -- Save current buffer or Q-Register
- * EWfile$
+ * EW$ -- Save buffer or Q-Register
+ * [n]EW[file]$
*
- * Saves the current buffer to disk.
+ * Saves the chosen buffer with id <n> to disk
+ * By default, the current buffer is saved.
* If the buffer was dirty, it will be clean afterwards.
* If the string argument <file> is not empty,
* the buffer is saved with the specified file name
* and is renamed in the ring.
*
- * The EW command also works if the current document
- * is a Q-Register, i.e. a Q-Register is edited.
- * In this case, the string contents of the current
- * Q-Register are saved to <file>.
+ * If the current document is a Q-Register and <n> is not given,
+ * the string contents of the current Q-Register are saved to <file>
+ * (cf. \fBE%\fIq\fR command)..
* Q-Registers have no notion of associated file names,
- * so <file> must be always specified.
+ * so <file> must be always specified in this case.
*
- * In interactive mode, EW is executed immediately and
+ * In interactive mode, \fBEW\fP is executed immediately and
* may be rubbed out.
* In order to support that, \*(ST creates so called
* save point files.
@@ -568,9 +708,9 @@ teco_state_save_file_done(teco_machine_main_t *ctx, const teco_string_t *str, GE
* Save point files are always created in the same directory
* as the original file to ensure that no copying of the file
* on disk is necessary but only a rename of the file.
- * When rubbing out the EW command, \*(ST restores the latest
+ * When rubbing out the \fBEW\fP command, \*(ST restores the latest
* save point file by moving (renaming) it back to its
- * original path \(em also not requiring any on-disk copying.
+ * original path -- also not requiring any on-disk copying.
* \*(ST is impossible to crash, but just in case it still
* does it may leave behind these save point files which
* must be manually deleted by the user.
@@ -580,62 +720,119 @@ teco_state_save_file_done(teco_machine_main_t *ctx, const teco_string_t *str, GE
* File names may also be tab-completed and string building
* characters are enabled by default.
*/
-TECO_DEFINE_STATE_EXPECTFILE(teco_state_save_file);
+TECO_DEFINE_STATE_EXPECTFILE(teco_state_save_file,
+ .expectstring.done_cb = teco_state_save_file_done
+);
+
+static teco_state_t *
+teco_state_read_file_done(teco_machine_main_t *ctx, 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,
+ .expectstring.done_cb = teco_state_read_file_done
+);
-/*$ EF close
- * [bool]EF -- Remove buffer from ring
+/*$ "EF" ":EF" close
+ * [n]EF -- Remove buffer from ring
* -EF
- * :EF
+ * [n]:EF
*
* Removes buffer from buffer ring, effectively
* closing it.
- * If the buffer is dirty (modified), EF will yield
+ * The optional argument <n> specifies the id of the buffer
+ * to close -- by default the current buffer will be closed.
+ * If the selected buffer is dirty (modified), \fBEF\fP will yield
* an error.
- * <bool> may be a specified to enforce closing dirty
- * buffers.
- * If it is a Failure condition boolean (negative),
- * the buffer will be closed unconditionally.
- * If <bool> is absent, the sign prefix (1 or -1) will
- * be implied, so \(lq-EF\(rq will always close the buffer.
+ * If <n> is negative (success boolean), buffer <-n> will be closed
+ * even if it is dirty.
+ * \(lq-EF\(rq will force-close the current buffer.
*
- * When colon-modified, <bool> is ignored and \fBEF\fP
- * will save the buffer before closing.
+ * When colon-modified, the selected buffer is saved before closing.
* The file is always written, unlike \(lq:EX\(rq which
* saves only dirty buffers.
* This can fail of course, e.g. when called on the unnamed
* buffer.
*
- * It is noteworthy that EF will be executed immediately in
+ * It is noteworthy that \fBEF\fP will be executed immediately in
* interactive mode but can be rubbed out at a later time
* to reopen the file.
* Closed files are kept in memory until the command line
* is terminated.
+ *
+ * Close and edit hooks are only executed when closing the current buffer.
*/
void
teco_state_ecommand_close(teco_machine_main_t *ctx, GError **error)
{
- if (teco_qreg_current) {
+ if (!teco_expressions_eval(FALSE, error))
+ return;
+
+ /*
+ * This is like implying teco_num_sign*teco_ring_get_id(teco_ring_current)
+ * but avoids the O(n) ring iterations.
+ */
+ teco_buffer_t *buffer;
+ gboolean force;
+ if (teco_expressions_args() > 0) {
+ teco_int_t id = teco_expressions_pop_num(0);
+ buffer = teco_ring_find(ABS(id));
+ if (!buffer) {
+ teco_error_invalidbuf_set(error, ABS(id));
+ return;
+ }
+ force = id < 0;
+ } else if (teco_qreg_current) {
+ /*
+ * TODO: Should perhaps remove the register like FQq.
+ */
const teco_string_t *name = &teco_qreg_current->head.name;
g_autofree gchar *name_printable = teco_string_echo(name->data, name->len);
g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED,
"Q-Register \"%s\" currently edited", name_printable);
return;
+ } else {
+ buffer = teco_ring_current;
+ force = teco_num_sign < 0;
+ teco_set_num_sign(1);
}
if (teco_machine_main_eval_colon(ctx) > 0) {
- if (!teco_buffer_save(teco_ring_current, NULL, error))
- return;
- } else {
- teco_int_t v;
- if (!teco_expressions_pop_num_calc(&v, teco_num_sign, error))
- return;
- if (teco_is_failure(v) && teco_ring_current->dirty) {
- g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED,
- "Buffer \"%s\" is dirty",
- teco_ring_current->filename ? : "(Unnamed)");
+ if (!teco_buffer_save(buffer, NULL, error))
return;
- }
+ } else if (!force && buffer->state > TECO_BUFFER_CLEAN) {
+ g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED,
+ "Buffer \"%s\" is dirty",
+ buffer->filename ? : "(Unnamed)");
+ return;
}
- teco_ring_close(error);
+ teco_ring_close(buffer, error);
}