aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/goto-commands.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/goto-commands.c')
-rw-r--r--src/goto-commands.c120
1 files changed, 81 insertions, 39 deletions
diff --git a/src/goto-commands.c b/src/goto-commands.c
index a0e6634..a9ff3c2 100644
--- a/src/goto-commands.c
+++ b/src/goto-commands.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
@@ -24,6 +24,7 @@
#include <glib.h>
#include "sciteco.h"
+#include "error.h"
#include "string-utils.h"
#include "expressions.h"
#include "parser.h"
@@ -34,17 +35,20 @@
#include "goto.h"
#include "goto-commands.h"
-TECO_DECLARE_STATE(teco_state_blockcomment);
-TECO_DECLARE_STATE(teco_state_eolcomment);
+static teco_state_t teco_state_blockcomment;
+static teco_state_t teco_state_eolcomment;
+/**
+ * In TECO_MODE_PARSE_ONLY_GOTO mode, we remain in parse-only mode
+ * until the given label is encountered.
+ */
teco_string_t teco_goto_skip_label = {NULL, 0};
-
-static gboolean
-teco_state_label_initial(teco_machine_main_t *ctx, GError **error)
-{
- memset(&ctx->goto_label, 0, sizeof(ctx->goto_label));
- return TRUE;
-}
+/**
+ * The program counter to restore if the teco_goto_skip_label
+ * is \b not found (after :Olabel$).
+ * If smaller than 0 an error is thrown instead.
+ */
+gssize teco_goto_backup_pc = -1;
/*
* NOTE: The comma is theoretically not allowed in a label
@@ -71,9 +75,10 @@ teco_state_label_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
teco_goto_table_undo_remove(&ctx->goto_table, ctx->goto_label.data, ctx->goto_label.len);
if (teco_goto_skip_label.len > 0 &&
- !teco_string_cmp(&ctx->goto_label, teco_goto_skip_label.data, teco_goto_skip_label.len)) {
+ !teco_string_cmp(ctx->goto_label, teco_goto_skip_label.data, teco_goto_skip_label.len)) {
teco_undo_string_own(teco_goto_skip_label);
memset(&teco_goto_skip_label, 0, sizeof(teco_goto_skip_label));
+ teco_undo_gssize(teco_goto_backup_pc) = -1;
if (ctx->parent.must_undo)
teco_undo_flags(ctx->flags);
@@ -108,28 +113,37 @@ teco_state_label_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
}
TECO_DEFINE_STATE(teco_state_label,
- .initial_cb = (teco_state_initial_cb_t)teco_state_label_initial,
- .style = SCE_SCITECO_LABEL
+ .style = SCE_SCITECO_LABEL,
+ .input_cb = (teco_state_input_cb_t)teco_state_label_input
);
static teco_state_t *
-teco_state_goto_done(teco_machine_main_t *ctx, const teco_string_t *str, GError **error)
+teco_state_goto_done(teco_machine_main_t *ctx, teco_string_t str, GError **error)
{
if (ctx->flags.mode > TECO_MODE_NORMAL)
return &teco_state_start;
+ if (!str.len) {
+ /* you can still write @O/,/, though... */
+ g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED,
+ "No labels given for <O>");
+ return NULL;
+ }
+
teco_int_t value;
- if (!teco_expressions_pop_num_calc(&value, 1, error))
+ if (!teco_expressions_pop_num_calc(&value, 0, error))
return NULL;
+ gboolean colon_modified = teco_machine_main_eval_colon(ctx) > 0;
+
/*
* Find the comma-separated substring in str indexed by `value`.
*/
teco_string_t label = {NULL, 0};
- while (value > 0) {
- label.data = label.data ? label.data+label.len+1 : str->data;
- const gchar *p = label.data ? memchr(label.data, ',', str->len - (label.data - str->data)) : NULL;
- label.len = p ? p - label.data : str->len - (label.data - str->data);
+ while (value >= 0) {
+ label.data = label.data ? label.data+label.len+1 : str.data;
+ const gchar *p = label.data ? memchr(label.data, ',', str.len - (label.data - str.data)) : NULL;
+ label.len = p ? p - label.data : str.len - (label.data - str.data);
value--;
@@ -137,19 +151,24 @@ teco_state_goto_done(teco_machine_main_t *ctx, const teco_string_t *str, GError
break;
}
- if (value == 0) {
+ if (value < 0 && label.len > 0) {
gssize pc = teco_goto_table_find(&ctx->goto_table, label.data, label.len);
if (pc >= 0) {
ctx->macro_pc = pc;
- } else {
+ } else if (!ctx->goto_table.complete) {
/* skip till label is defined */
g_assert(teco_goto_skip_label.len == 0);
undo__teco_string_truncate(&teco_goto_skip_label, 0);
teco_string_init(&teco_goto_skip_label, label.data, label.len);
+ teco_undo_gssize(teco_goto_backup_pc) = colon_modified ? ctx->macro_pc : -1;
if (ctx->parent.must_undo)
teco_undo_flags(ctx->flags);
ctx->flags.mode = TECO_MODE_PARSE_ONLY_GOTO;
+ } else if (!colon_modified) {
+ /* can happen if we previously executed a colon-modified go-to */
+ teco_error_label_set(error, teco_goto_skip_label.data, teco_goto_skip_label.len);
+ return NULL;
}
}
@@ -159,38 +178,52 @@ teco_state_goto_done(teco_machine_main_t *ctx, const teco_string_t *str, GError
/* in cmdline.c */
gboolean teco_state_goto_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx,
gunichar chr, GError **error);
-gboolean teco_state_goto_insert_completion(teco_machine_main_t *ctx, const teco_string_t *str,
+gboolean teco_state_goto_insert_completion(teco_machine_main_t *ctx, teco_string_t str,
GError **error);
-/*$ O
+/*$ "O" ":O" goto
* Olabel$ -- Go to label
- * [n]Olabel1[,label2,...]$
+ * :Olabel$
+ * [n]Olabel0[,label1,...]$
*
* Go to <label>.
* The simple go-to command is a special case of the
* computed go-to command.
* A comma-separated list of labels may be specified
* in the string argument.
- * The label to jump to is selected by <n> (1 is <label1>,
- * 2 is <label2>, etc.).
- * If <n> is omitted, 1 is implied.
+ * The label to jump to is selected by <n> (0 is <label0>,
+ * 1 is <label1>, etc.).
+ * If <n> is omitted, 0 is implied.
+ * Computed go-tos can be used like switch-case statements
+ * other languages.
*
* If the label selected by <n> is does not exist in the
- * list of labels, the command does nothing.
+ * list of labels or is empty, the command does nothing
+ * and execution continues normally.
* Label definitions are cached in a table, so that
* if the label to go to has already been defined, the
* go-to command will jump immediately.
* Otherwise, parsing continues until the <label>
* is defined.
* The command will yield an error if a label has
- * not been defined when the macro or command-line
- * is terminated.
- * In the latter case, the user will not be able to
- * terminate the command-line.
+ * not been defined when the macro is terminated.
+ * When jumping to a non-existent <label> in the
+ * command-line macro, you cannot practically terminate
+ * the command-line until defining the <label>.
+ *
+ * String building constructs are enabled in \fBO\fP
+ * which allows for a second kind of computed go-to,
+ * where the label name contains the value to select.
+ * When colon-modifying the \fBO\fP command, execution
+ * will continue after the command if the given <label>
+ * isn't found.
+ * This is useful to handle the \(lqdefault\(rq case
+ * when using computed go-tos of the second kind.
*/
TECO_DEFINE_STATE_EXPECTSTRING(teco_state_goto,
.process_edit_cmd_cb = (teco_state_process_edit_cmd_cb_t)teco_state_goto_process_edit_cmd,
- .insert_completion_cb = (teco_state_insert_completion_cb_t)teco_state_goto_insert_completion
+ .insert_completion_cb = (teco_state_insert_completion_cb_t)teco_state_goto_insert_completion,
+ .expectstring.done_cb = teco_state_goto_done
);
/**
@@ -211,25 +244,34 @@ TECO_DEFINE_STATE_EXPECTSTRING(teco_state_goto,
)
static teco_state_t *
-teco_state_blockcomment_star_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
+teco_state_blockcomment_star_input(teco_machine_t *ctx, gunichar chr, GError **error)
{
return chr == '!' ? &teco_state_start : &teco_state_blockcomment;
}
-TECO_DEFINE_STATE_COMMENT(teco_state_blockcomment_star);
+static TECO_DEFINE_STATE_COMMENT(teco_state_blockcomment_star,
+ .input_cb = teco_state_blockcomment_star_input
+);
static teco_state_t *
-teco_state_blockcomment_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
+teco_state_blockcomment_input(teco_machine_t *ctx, gunichar chr, GError **error)
{
return chr == '*' ? &teco_state_blockcomment_star : &teco_state_blockcomment;
}
-TECO_DEFINE_STATE_COMMENT(teco_state_blockcomment);
+static TECO_DEFINE_STATE_COMMENT(teco_state_blockcomment,
+ .input_cb = teco_state_blockcomment_input
+);
+/*
+ * `!!` line comments are inspired by TECO-64.
+ */
static teco_state_t *
-teco_state_eolcomment_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
+teco_state_eolcomment_input(teco_machine_t *ctx, gunichar chr, GError **error)
{
return chr == '\n' ? &teco_state_start : &teco_state_eolcomment;
}
-TECO_DEFINE_STATE_COMMENT(teco_state_eolcomment);
+static TECO_DEFINE_STATE_COMMENT(teco_state_eolcomment,
+ .input_cb = teco_state_eolcomment_input
+);