aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorRobin Haberkorn <robin.haberkorn@googlemail.com>2025-07-06 06:37:07 +0200
committerRobin Haberkorn <robin.haberkorn@googlemail.com>2025-07-11 01:52:02 +0300
commit2baa14add6d9976c29b27cf4470bb458a0198694 (patch)
tree283171a833f0a91a4c20861c452b81340ac0e0cd
parentce1a2b1b428e95d300af478fb0b36ca3773f7f84 (diff)
downloadsciteco-2baa14add6d9976c29b27cf4470bb458a0198694.tar.gz
<EF> supports a numeric buffer id now
* ED hooks are not executed in this case * <EF> is now allowed even when editing a Q-Reg, unless you try to close the current buffer
-rw-r--r--TODO4
-rwxr-xr-xdoc/tedoc.tes4
-rw-r--r--src/core-commands.c2
-rw-r--r--src/ring.c119
-rw-r--r--src/ring.h2
-rw-r--r--tests/testsuite.at6
6 files changed, 88 insertions, 49 deletions
diff --git a/TODO b/TODO
index 697dd10..8178671 100644
--- a/TODO
+++ b/TODO
@@ -318,7 +318,6 @@ Features:
rubbed out command line).
* some missing useful VideoTECO/TECO-11 commands and unnecessary
incompatibilities:
- * EF with buffer id
* ER command: read file(s) into current buffer at dot.
Video TECO accepts a glob pattern here.
* nEW to save a buffer by id
@@ -701,6 +700,9 @@ Features:
Allows us to do CI on FreeBSD and potentially other more obscure systems.
Also, this will allow us to migrate to another hoster like
Savannah or Sourcehut later on.
+ * nEF is currently disallowed when editing a Q-Reg as long as
+ n == Q* (default). Perhaps it should always be allowed without
+ switching from the Q-Reg view.
Optimizations:
* Use SC_DOCUMENTOPTION_STYLES_NONE in batch mode.
diff --git a/doc/tedoc.tes b/doc/tedoc.tes
index b148691..885f41a 100755
--- a/doc/tedoc.tes
+++ b/doc/tedoc.tes
@@ -97,11 +97,11 @@ I\#
\#^J
!* find insertion point *!
-FS^J.TEDOC^J^J
+:FS^J.TEDOC^J^J"F(0/0)'
EJ-1<
<
- 2U* EU.#scQ*
+ 2U* [*]#sc
!* extract comment *!
SQ[comment.start]$;
diff --git a/src/core-commands.c b/src/core-commands.c
index 819b1aa..0ff2b81 100644
--- a/src/core-commands.c
+++ b/src/core-commands.c
@@ -2003,7 +2003,7 @@ teco_state_ecommand_flags(teco_machine_main_t *ctx, GError **error)
* The current user interface: 1 for Curses, 2 for GTK
* (\fBread-only\fP)
* .IP 1:
- * The current numbfer of buffers: Also the numeric id
+ * The current number of buffers: Also the numeric id
* of the last buffer in the ring. This is implied if
* no argument is given, so \(lqEJ\(rq returns the number
* of buffers in the ring.
diff --git a/src/ring.c b/src/ring.c
index b7a75d1..1c9a2cd 100644
--- a/src/ring.c
+++ b/src/ring.c
@@ -151,7 +151,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 +162,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;
@@ -320,7 +317,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 +330,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
@@ -583,38 +596,59 @@ teco_state_save_file_done(teco_machine_main_t *ctx, const teco_string_t *str, GE
TECO_DEFINE_STATE_EXPECTFILE(teco_state_save_file);
/*$ EF close
- * [bool]EF -- Remove buffer from ring
+ * [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;
+ if (!teco_expressions_pop_num_calc(&id, 0, error))
+ return;
+ buffer = teco_ring_find(ABS(id));
+ force = id < 0;
+ } else {
+ buffer = teco_ring_current;
+ force = teco_num_sign < 0;
+ teco_set_num_sign(1);
+ }
+
+ if (buffer == teco_ring_current && teco_qreg_current) {
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,
@@ -623,19 +657,16 @@ teco_state_ecommand_close(teco_machine_main_t *ctx, GError **error)
}
if (teco_machine_main_eval_colon(ctx) > 0) {
- if (!teco_buffer_save(teco_ring_current, NULL, error))
+ if (!teco_buffer_save(buffer, 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) {
+ if (!force && buffer->dirty) {
g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED,
"Buffer \"%s\" is dirty",
- teco_ring_current->filename ? : "(Unnamed)");
+ buffer->filename ? : "(Unnamed)");
return;
}
}
- teco_ring_close(error);
+ teco_ring_close(buffer, error);
}
diff --git a/src/ring.h b/src/ring.h
index 34293b9..b75846a 100644
--- a/src/ring.h
+++ b/src/ring.h
@@ -86,7 +86,7 @@ teco_ring_undo_edit(void)
teco_buffer_undo_edit(teco_ring_current);
}
-gboolean teco_ring_close(GError **error);
+gboolean teco_ring_close(teco_buffer_t *buffer, GError **error);
void teco_ring_undo_close(void);
void teco_ring_set_scintilla_undo(gboolean state);
diff --git a/tests/testsuite.at b/tests/testsuite.at
index a1b8ada..b0db94d 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -239,6 +239,12 @@ TE_CHECK([[@^Ua/test/ @E%a/saveqreg.txt/ @EB/saveqreg.txt/ Z-4"N(0/0)']], 0, ign
TE_CHECK([[@E%$/saveqreg.txt/ @EB/saveqreg.txt/ Z-:Q$"N(0/0)']], 0, ignore, ignore)
AT_CLEANUP
+AT_SETUP([Opening/closing buffers])
+TE_CHECK([[@EB/foo/ @I/XXX/ -EF :Q*"N(0/0)']], 0, ignore, ignore)
+TE_CHECK([[@EB/foo/ @I/XXX/ :EF :Q*"N(0/0)' @EB/foo/ Z-3"N(0/0)']], 0, ignore, ignore)
+TE_CHECK([[@EB/foo/ 1EF :Q*"=(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)