aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/qreg-commands.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/qreg-commands.c')
-rw-r--r--src/qreg-commands.c235
1 files changed, 124 insertions, 111 deletions
diff --git a/src/qreg-commands.c b/src/qreg-commands.c
index a4019a0..4ede403 100644
--- a/src/qreg-commands.c
+++ b/src/qreg-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
@@ -39,8 +39,8 @@ teco_state_expectqreg_initial(teco_machine_main_t *ctx, GError **error)
teco_state_t *current = ctx->parent.current;
/*
- * NOTE: We have to allocate a new instance always since `expectqreg`
- * is part of an union.
+ * NOTE: This could theoretically be allocated once in
+ * teco_machine_main_init(), but we'd have to set the type here anyway.
*/
ctx->expectqreg = teco_machine_qregspec_new(current->expectqreg.type, ctx->qreg_table_locals,
ctx->parent.must_undo);
@@ -54,8 +54,8 @@ teco_state_expectqreg_input(teco_machine_main_t *ctx, gunichar chr, GError **err
{
teco_state_t *current = ctx->parent.current;
- teco_qreg_t *qreg;
- teco_qreg_table_t *table;
+ teco_qreg_t *qreg = NULL;
+ teco_qreg_table_t *table = NULL;
switch (teco_machine_qregspec_input(ctx->expectqreg, chr,
ctx->flags.mode == TECO_MODE_NORMAL ? &qreg : NULL, &table, error)) {
@@ -69,7 +69,7 @@ teco_state_expectqreg_input(teco_machine_main_t *ctx, gunichar chr, GError **err
/*
* NOTE: ctx->expectqreg is preserved since we may want to query it from follow-up
- * states. This means, it must usually be stored manually in got_register_cb() via:
+ * states. This means, it must usually be reset manually in got_register_cb() via:
* teco_state_expectqreg_reset(ctx);
*/
return current->expectqreg.got_register_cb(ctx, qreg, table, error);
@@ -91,7 +91,9 @@ teco_state_pushqreg_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg,
* Save Q-Register <q> contents on the global Q-Register push-down
* stack.
*/
-TECO_DEFINE_STATE_EXPECTQREG(teco_state_pushqreg);
+TECO_DEFINE_STATE_EXPECTQREG(teco_state_pushqreg,
+ .expectqreg.got_register_cb = teco_state_pushqreg_got_register
+);
static teco_state_t *
teco_state_popqreg_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg,
@@ -99,25 +101,37 @@ teco_state_popqreg_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg,
{
teco_state_expectqreg_reset(ctx);
- return ctx->flags.mode == TECO_MODE_NORMAL &&
- !teco_qreg_stack_pop(qreg, error) ? NULL : &teco_state_start;
+ if (ctx->flags.mode > TECO_MODE_NORMAL)
+ return &teco_state_start;
+
+ if (!teco_machine_main_eval_colon(ctx))
+ return !teco_qreg_stack_pop(qreg, error) ? NULL : &teco_state_start;
+ teco_expressions_push(teco_bool(teco_qreg_stack_pop(qreg, NULL)));
+ return &teco_state_start;
}
-/*$ "]" "]q" pop
+/*$ "]" "]q" ":]q" pop
* ]q -- Restore Q-Register
+ * :]q -> Success|Failure
*
* Restore Q-Register <q> by replacing its contents
* with the contents of the register saved on top of
* the Q-Register push-down stack.
* The stack entry is popped.
*
+ * When colon-modified, \fB]\fP returns a success boolean
+ * (-1) if there was a register to pop.
+ * If the stack was empty, a failure boolean (0) is returned
+ * instead of throwing an error.
+ *
* In interactive mode, the original contents of <q>
* are not immediately reclaimed but are kept in memory
* to support rubbing out the command.
* Memory is reclaimed on command-line termination.
*/
TECO_DEFINE_STATE_EXPECTQREG(teco_state_popqreg,
- .expectqreg.type = TECO_QREG_OPTIONAL_INIT
+ .expectqreg.type = TECO_QREG_OPTIONAL_INIT,
+ .expectqreg.got_register_cb = teco_state_popqreg_got_register
);
static teco_state_t *
@@ -131,11 +145,12 @@ teco_state_eqcommand_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg,
}
TECO_DEFINE_STATE_EXPECTQREG(teco_state_eqcommand,
- .expectqreg.type = TECO_QREG_OPTIONAL_INIT
+ .expectqreg.type = TECO_QREG_OPTIONAL_INIT,
+ .expectqreg.got_register_cb = teco_state_eqcommand_got_register
);
static teco_state_t *
-teco_state_loadqreg_done(teco_machine_main_t *ctx, const teco_string_t *str, GError **error)
+teco_state_loadqreg_done(teco_machine_main_t *ctx, teco_string_t str, GError **error)
{
teco_qreg_t *qreg;
teco_qreg_table_t *table;
@@ -146,9 +161,9 @@ teco_state_loadqreg_done(teco_machine_main_t *ctx, const teco_string_t *str, GEr
if (ctx->flags.mode > TECO_MODE_NORMAL)
return &teco_state_start;
- if (str->len > 0) {
+ if (str.len > 0) {
/* Load file into Q-Register */
- g_autofree gchar *filename = teco_file_expand_path(str->data);
+ g_autofree gchar *filename = teco_file_expand_path(str.data);
if (!qreg->vtable->load(qreg, filename, error))
return NULL;
} else {
@@ -176,7 +191,9 @@ teco_state_loadqreg_done(teco_machine_main_t *ctx, const teco_string_t *str, GEr
* Undefined Q-Registers will be defined.
* The command fails if <file> could not be read.
*/
-TECO_DEFINE_STATE_EXPECTFILE(teco_state_loadqreg);
+TECO_DEFINE_STATE_EXPECTFILE(teco_state_loadqreg,
+ .expectstring.done_cb = teco_state_loadqreg_done
+);
static teco_state_t *
teco_state_epctcommand_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg,
@@ -188,10 +205,12 @@ teco_state_epctcommand_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg,
return &teco_state_saveqreg;
}
-TECO_DEFINE_STATE_EXPECTQREG(teco_state_epctcommand);
+TECO_DEFINE_STATE_EXPECTQREG(teco_state_epctcommand,
+ .expectqreg.got_register_cb = teco_state_epctcommand_got_register
+);
static teco_state_t *
-teco_state_saveqreg_done(teco_machine_main_t *ctx, const teco_string_t *str, GError **error)
+teco_state_saveqreg_done(teco_machine_main_t *ctx, teco_string_t str, GError **error)
{
teco_qreg_t *qreg;
@@ -201,7 +220,7 @@ teco_state_saveqreg_done(teco_machine_main_t *ctx, const teco_string_t *str, GEr
if (ctx->flags.mode > TECO_MODE_NORMAL)
return &teco_state_start;
- g_autofree gchar *filename = teco_file_expand_path(str->data);
+ g_autofree gchar *filename = teco_file_expand_path(str.data);
return qreg->vtable->save(qreg, filename, error) ? &teco_state_start : NULL;
}
@@ -220,7 +239,9 @@ teco_state_saveqreg_done(teco_machine_main_t *ctx, const teco_string_t *str, GEr
* File names may also be tab-completed and string building
* characters are enabled by default.
*/
-TECO_DEFINE_STATE_EXPECTFILE(teco_state_saveqreg);
+TECO_DEFINE_STATE_EXPECTFILE(teco_state_saveqreg,
+ .expectstring.done_cb = teco_state_saveqreg_done
+);
static gboolean
teco_state_queryqreg_initial(teco_machine_main_t *ctx, GError **error)
@@ -273,9 +294,7 @@ teco_state_queryqreg_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg,
if (teco_expressions_args() > 0) {
/* Query character from Q-Register string */
- teco_int_t pos;
- if (!teco_expressions_pop_num_calc(&pos, 0, error))
- return NULL;
+ teco_int_t pos = teco_expressions_pop_num(0);
if (pos < 0) {
teco_error_range_set(error, "Q");
return NULL;
@@ -297,24 +316,24 @@ teco_state_queryqreg_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg,
return &teco_state_start;
}
-/*$ Q Qq query
+/*$ "Q" "Qq" ":Qq" query
* Qq -> n -- Query Q-Register existence, its integer or string characters
* -Qq -> -n
- * <position>Qq -> character
+ * <position>Qq -> code
* :Qq -> -1 | size
*
* Without any arguments, get and return the integer-part of
* Q-Register <q>.
*
- * With one argument, return the <character> code at <position>
+ * With one argument, return the character <code> at <position>
* from the string-part of Q-Register <q>.
* Positions are handled like buffer positions \(em they
* begin at 0 up to the length of the string minus 1.
- * An error is thrown for invalid positions.
+ * -1 is returned for invalid positions.
* If <q> is encoded as UTF-8 and there is
- * an incomplete sequence at the requested position,
- * -1 is returned.
- * All other invalid Unicode sequences are returned as -2.
+ * an invalid byte sequence at the requested position,
+ * -2 is returned.
+ * Incomplete UTF-8 byte sequences are returned as -3.
* Both non-colon-modified forms of Q require register <q>
* to be defined and fail otherwise.
*
@@ -337,7 +356,8 @@ teco_state_queryqreg_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg,
* boolean.
*/
TECO_DEFINE_STATE_EXPECTQREG(teco_state_queryqreg,
- .initial_cb = (teco_state_initial_cb_t)teco_state_queryqreg_initial
+ .initial_cb = (teco_state_initial_cb_t)teco_state_queryqreg_initial,
+ .expectqreg.got_register_cb = teco_state_queryqreg_got_register
);
static teco_state_t *
@@ -351,12 +371,13 @@ teco_state_ctlucommand_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg,
}
TECO_DEFINE_STATE_EXPECTQREG(teco_state_ctlucommand,
- .expectqreg.type = TECO_QREG_OPTIONAL_INIT
+ .expectqreg.type = TECO_QREG_OPTIONAL_INIT,
+ .expectqreg.got_register_cb = teco_state_ctlucommand_got_register
);
static teco_state_t *
teco_state_setqregstring_nobuilding_done(teco_machine_main_t *ctx,
- const teco_string_t *str, GError **error)
+ teco_string_t str, GError **error)
{
teco_qreg_t *qreg;
@@ -378,43 +399,46 @@ teco_state_setqregstring_nobuilding_done(teco_machine_main_t *ctx,
return NULL;
g_autofree gchar *buffer = NULL;
+ const gchar *start;
gsize len = 0;
if (codepage == SC_CP_UTF8) {
- /* the glib docs wrongly claim that one character can take 6 bytes */
- buffer = g_malloc(4*args);
+ /* 4 bytes should be enough for UTF-8, but we better follow the documentation */
+ start = buffer = g_malloc(args*6);
+
for (gint i = args; i > 0; i--) {
- teco_int_t v;
- if (!teco_expressions_pop_num_calc(&v, 0, error))
- return NULL;
- if (v < 0 || !g_unichar_validate(v)) {
+ teco_int_t chr = teco_expressions_peek_num(i-1);
+ if (chr < 0 || !g_unichar_validate(chr)) {
teco_error_codepoint_set(error, "^U");
return NULL;
}
- len += g_unichar_to_utf8(v, buffer+len);
+ len += g_unichar_to_utf8(chr, buffer+len);
}
+ /* we pop only now since we had to peek in reverse order */
+ for (gint i = 0; i < args; i++)
+ teco_expressions_pop_num(0);
} else {
buffer = g_malloc(args);
- for (gint i = args; i > 0; i--) {
- teco_int_t v;
- if (!teco_expressions_pop_num_calc(&v, 0, error))
- return NULL;
- if (v < 0 || v > 0xFF) {
+
+ for (gint i = 0; i < args; i++) {
+ teco_int_t chr = teco_expressions_pop_num(0);
+ if (chr < 0 || chr > 0xFF) {
teco_error_codepoint_set(error, "^U");
return NULL;
}
- buffer[len++] = v;
+ buffer[args-(++len)] = chr;
}
+ start = buffer+args-len;
}
if (colon_modified) {
/* append to register */
- if (!qreg->vtable->append_string(qreg, buffer, len, error))
+ if (!qreg->vtable->append_string(qreg, start, len, error))
return NULL;
} else {
/* set register */
if (!qreg->vtable->undo_set_string(qreg, error) ||
- !qreg->vtable->set_string(qreg, buffer, len,
+ !qreg->vtable->set_string(qreg, start, len,
codepage, error))
return NULL;
}
@@ -422,12 +446,12 @@ teco_state_setqregstring_nobuilding_done(teco_machine_main_t *ctx,
if (args > 0 || colon_modified) {
/* append to register */
- if (!qreg->vtable->append_string(qreg, str->data, str->len, error))
+ if (!qreg->vtable->append_string(qreg, str.data, str.len, error))
return NULL;
} else {
/* set register */
if (!qreg->vtable->undo_set_string(qreg, error) ||
- !qreg->vtable->set_string(qreg, str->data, str->len,
+ !qreg->vtable->set_string(qreg, str.data, str.len,
teco_default_codepage(), error))
return NULL;
}
@@ -435,7 +459,7 @@ teco_state_setqregstring_nobuilding_done(teco_machine_main_t *ctx,
return &teco_state_start;
}
-/*$ ^Uq
+/*$ "^Uq" ":^Uq" "set string" append
* [c1,c2,...]^Uq[string]$ -- Set or append to Q-Register string without string building
* [c1,c2,...]:^Uq[string]$
*
@@ -462,7 +486,8 @@ teco_state_setqregstring_nobuilding_done(teco_machine_main_t *ctx,
* is desired.
*/
TECO_DEFINE_STATE_EXPECTSTRING(teco_state_setqregstring_nobuilding,
- .expectstring.string_building = FALSE
+ .expectstring.string_building = FALSE,
+ .expectstring.done_cb = teco_state_setqregstring_nobuilding_done
);
static teco_state_t *
@@ -476,7 +501,8 @@ teco_state_eucommand_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg,
}
TECO_DEFINE_STATE_EXPECTQREG(teco_state_eucommand,
- .expectqreg.type = TECO_QREG_OPTIONAL_INIT
+ .expectqreg.type = TECO_QREG_OPTIONAL_INIT,
+ .expectqreg.got_register_cb = teco_state_eucommand_got_register
);
static gboolean
@@ -499,13 +525,7 @@ teco_state_setqregstring_building_initial(teco_machine_main_t *ctx, GError **err
return TRUE;
}
-static teco_state_t *
-teco_state_setqregstring_building_done(teco_machine_main_t *ctx, const teco_string_t *str, GError **error)
-{
- return teco_state_setqregstring_nobuilding_done(ctx, str, error);
-}
-
-/*$ EU EUq
+/*$ "EU" "EUq" ":EUq"
* [c1,c2,...]EUq[string]$ -- Set or append to Q-Register string with string building characters
* [c1,c2,...]:EUq[string]$
*
@@ -517,7 +537,8 @@ teco_state_setqregstring_building_done(teco_machine_main_t *ctx, const teco_stri
*/
TECO_DEFINE_STATE_EXPECTSTRING(teco_state_setqregstring_building,
.initial_cb = (teco_state_initial_cb_t)teco_state_setqregstring_building_initial,
- .expectstring.string_building = TRUE
+ .expectstring.string_building = TRUE,
+ .expectstring.done_cb = teco_state_setqregstring_nobuilding_done
);
static teco_state_t *
@@ -534,9 +555,12 @@ teco_state_getqregstring_got_register(teco_machine_main_t *ctx, teco_qreg_t *qre
if (!qreg->vtable->get_string(qreg, &str.data, &str.len, NULL, error))
return NULL;
- teco_undo_gsize(teco_ranges[0].from) = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0);
- teco_undo_gsize(teco_ranges[0].to) = teco_ranges[0].from + str.len;
- teco_undo_guint(teco_ranges_count) = 1;
+ if (teco_machine_main_eval_colon(ctx)) {
+ teco_interface_msg_literal(TECO_MSG_USER, str.data, str.len);
+ return &teco_state_start;
+ }
+
+ sptr_t pos = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0);
if (str.len > 0) {
teco_interface_ssm(SCI_BEGINUNDOACTION, 0, 0);
@@ -548,17 +572,28 @@ teco_state_getqregstring_got_register(teco_machine_main_t *ctx, teco_qreg_t *qre
undo__teco_interface_ssm(SCI_UNDO, 0, 0);
}
+ teco_undo_int(teco_ranges[0].from) = teco_interface_bytes2glyphs(pos);
+ teco_undo_int(teco_ranges[0].to) = teco_interface_bytes2glyphs(pos + str.len);
+ teco_undo_guint(teco_ranges_count) = 1;
+
return &teco_state_start;
}
-/*$ G Gq get
- * Gq -- Insert Q-Register string
+/*$ G Gq get paste
+ * Gq -- Insert or print Q-Register string
+ * :Gq
*
* Inserts the string of Q-Register <q> into the buffer
* at its current position.
+ * If colon-modified prints the string as a message
+ * (i.e. to the terminal and/or in the message area) instead
+ * of modifying the current buffer.
+ *
* Specifying an undefined <q> yields an error.
*/
-TECO_DEFINE_STATE_EXPECTQREG(teco_state_getqregstring);
+TECO_DEFINE_STATE_EXPECTQREG(teco_state_getqregstring,
+ .expectqreg.got_register_cb = teco_state_getqregstring_got_register
+);
static teco_state_t *
teco_state_setqreginteger_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg,
@@ -590,7 +625,7 @@ teco_state_setqreginteger_got_register(teco_machine_main_t *ctx, teco_qreg_t *qr
return &teco_state_start;
}
-/*$ U Uq
+/*$ "U" "Uq" ":Uq" set
* nUq -- Set Q-Register integer
* -Uq
* [n]:Uq -> Success|Failure
@@ -607,7 +642,8 @@ teco_state_setqreginteger_got_register(teco_machine_main_t *ctx, teco_qreg_t *qr
* The register is defined if it does not exist.
*/
TECO_DEFINE_STATE_EXPECTQREG(teco_state_setqreginteger,
- .expectqreg.type = TECO_QREG_OPTIONAL_INIT
+ .expectqreg.type = TECO_QREG_OPTIONAL_INIT,
+ .expectqreg.got_register_cb = teco_state_setqreginteger_got_register
);
static teco_state_t *
@@ -641,7 +677,8 @@ teco_state_increaseqreg_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg
* <q> will be defined if it does not exist.
*/
TECO_DEFINE_STATE_EXPECTQREG(teco_state_increaseqreg,
- .expectqreg.type = TECO_QREG_OPTIONAL_INIT
+ .expectqreg.type = TECO_QREG_OPTIONAL_INIT,
+ .expectqreg.got_register_cb = teco_state_increaseqreg_got_register
);
static teco_state_t *
@@ -674,7 +711,7 @@ teco_state_macro_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg,
return &teco_state_start;
}
-/*$ M Mq eval
+/*$ "M" "Mq" ":Mq" call eval macro
* Mq -- Execute macro
* :Mq
*
@@ -700,15 +737,17 @@ teco_state_macro_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg,
* (as reported by \fBEE\fP), its contents must be and are checked to be in
* valid UTF-8.
*/
-TECO_DEFINE_STATE_EXPECTQREG(teco_state_macro);
+TECO_DEFINE_STATE_EXPECTQREG(teco_state_macro,
+ .expectqreg.got_register_cb = teco_state_macro_got_register
+);
static teco_state_t *
-teco_state_macrofile_done(teco_machine_main_t *ctx, const teco_string_t *str, GError **error)
+teco_state_indirect_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);
+ g_autofree gchar *filename = teco_file_expand_path(str.data);
if (teco_machine_main_eval_colon(ctx) > 0) {
/* don't create new local Q-Registers if colon modifier is given */
@@ -725,9 +764,9 @@ teco_state_macrofile_done(teco_machine_main_t *ctx, const teco_string_t *str, GE
return &teco_state_start;
}
-/*$ EM
- * EMfile$ -- Execute macro from file
- * :EMfile$
+/*$ "EI" ":EI" indirect include
+ * EIfile$ -- Execute from indirect command file
+ * :EIfile$
*
* Read the file with name <file> into memory and execute its contents
* as a macro.
@@ -738,7 +777,9 @@ teco_state_macrofile_done(teco_machine_main_t *ctx, const teco_string_t *str, GE
* As all \*(ST code, the contents of <file> must be in valid UTF-8
* even if operating in the \(lqdefault ANSI\(rq mode as configured by \fBED\fP.
*/
-TECO_DEFINE_STATE_EXPECTFILE(teco_state_macrofile);
+TECO_DEFINE_STATE_EXPECTFILE(teco_state_indirect,
+ .expectstring.done_cb = teco_state_indirect_done
+);
static teco_state_t *
teco_state_copytoqreg_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg,
@@ -757,39 +798,10 @@ teco_state_copytoqreg_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg,
if (ctx->flags.mode > TECO_MODE_NORMAL)
return &teco_state_start;
- gssize from, len; /* in bytes */
+ gsize from, len;
- if (!teco_expressions_eval(FALSE, error))
+ if (!teco_get_range_args("X", &from, &len, error))
return NULL;
- if (teco_expressions_args() <= 1) {
- teco_int_t line;
-
- from = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0);
- if (!teco_expressions_pop_num_calc(&line, teco_num_sign, error))
- return NULL;
- line += teco_interface_ssm(SCI_LINEFROMPOSITION, from, 0);
-
- if (!teco_validate_line(line)) {
- teco_error_range_set(error, "X");
- return NULL;
- }
-
- len = teco_interface_ssm(SCI_POSITIONFROMLINE, line, 0) - from;
-
- if (len < 0) {
- from += len;
- len *= -1;
- }
- } else {
- gssize to = teco_interface_glyphs2bytes(teco_expressions_pop_num(0));
- from = teco_interface_glyphs2bytes(teco_expressions_pop_num(0));
- len = to - from;
-
- if (len < 0 || from < 0 || to < 0) {
- teco_error_range_set(error, "X");
- return NULL;
- }
- }
/*
* NOTE: This does not use SCI_GETRANGEPOINTER+SCI_GETGAPPOSITION
@@ -837,7 +849,7 @@ teco_state_copytoqreg_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg,
return &teco_state_start;
}
-/*$ X Xq
+/*$ "X" "Xq" ":Xq" "@Xq" ":@Xq" copy extract
* [lines]Xq -- Copy into or append or cut to Q-Register
* -Xq
* from,toXq
@@ -870,5 +882,6 @@ teco_state_copytoqreg_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg,
* Register <q> will be created if it is undefined.
*/
TECO_DEFINE_STATE_EXPECTQREG(teco_state_copytoqreg,
- .expectqreg.type = TECO_QREG_OPTIONAL_INIT
+ .expectqreg.type = TECO_QREG_OPTIONAL_INIT,
+ .expectqreg.got_register_cb = teco_state_copytoqreg_got_register
);