aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core-commands.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core-commands.c')
-rw-r--r--src/core-commands.c184
1 files changed, 120 insertions, 64 deletions
diff --git a/src/core-commands.c b/src/core-commands.c
index 81d5869..6c4ab58 100644
--- a/src/core-commands.c
+++ b/src/core-commands.c
@@ -52,6 +52,7 @@
static teco_state_t *teco_state_control_input(teco_machine_main_t *ctx, gunichar chr, GError **error);
static teco_state_t *teco_state_ctlc_control_input(teco_machine_main_t *ctx, gunichar chr, GError **error);
+static teco_state_t *teco_state_endcond(teco_machine_main_t *ctx, const gchar *cmd, GError **error);
/**
* Translate buffer range arguments from the expression stack to
@@ -72,7 +73,8 @@ static teco_state_t *teco_state_ctlc_control_input(teco_machine_main_t *ctx, gun
* But it needs to discern between invalid ranges and other errors.
*/
gboolean
-teco_get_range_args(const gchar *cmd, gsize *from_ret, gsize *len_ret, GError **error)
+teco_get_range_args(const gchar *cmd, teco_int_t *from_glyphs_ret, gsize *from_ret,
+ teco_int_t *len_glyphs_ret, gsize *len_ret, GError **error)
{
gssize from, len; /* in bytes */
@@ -99,15 +101,27 @@ teco_get_range_args(const gchar *cmd, gsize *from_ret, gsize *len_ret, GError **
from += len;
len *= -1;
}
+
+ if (from_glyphs_ret)
+ *from_glyphs_ret = teco_interface_bytes2glyphs_absdot(from);
+ if (len_glyphs_ret)
+ *len_glyphs_ret = teco_interface_bytes2glyphs_absdot(from+len);
} else {
- gssize to = teco_interface_glyphs2bytes(teco_expressions_pop_num(0));
- from = teco_interface_glyphs2bytes(teco_expressions_pop_num(0));
+ teco_int_t to_glyphs = teco_expressions_pop_num(0);
+ gssize to = teco_interface_glyphs2bytes_absdot(to_glyphs);
+ teco_int_t from_glyphs = teco_expressions_pop_num(0);
+ from = teco_interface_glyphs2bytes_absdot(from_glyphs);
len = to - from;
if (len < 0 || from < 0 || to < 0) {
teco_error_range_set(error, cmd);
return FALSE;
}
+
+ if (from_glyphs_ret)
+ *from_glyphs_ret = from_glyphs;
+ if (len_glyphs_ret)
+ *len_glyphs_ret = to_glyphs - from_glyphs;
}
*from_ret = from;
@@ -199,8 +213,7 @@ teco_state_start_dot(teco_machine_main_t *ctx, GError **error)
{
if (!teco_expressions_eval(FALSE, error))
return;
- sptr_t pos = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0);
- teco_expressions_push(teco_interface_bytes2glyphs(pos));
+ teco_expressions_push(teco_current_doc_get_dot());
}
/*$ Z size
@@ -217,7 +230,7 @@ teco_state_start_zed(teco_machine_main_t *ctx, GError **error)
if (!teco_expressions_eval(FALSE, error))
return;
sptr_t pos = teco_interface_ssm(SCI_GETLENGTH, 0, 0);
- teco_expressions_push(teco_interface_bytes2glyphs(pos));
+ teco_expressions_push(teco_interface_bytes2glyphs_absdot(pos));
}
/*$ H
@@ -235,7 +248,7 @@ teco_state_start_range(teco_machine_main_t *ctx, GError **error)
return;
teco_expressions_push(0);
sptr_t pos = teco_interface_ssm(SCI_GETLENGTH, 0, 0);
- teco_expressions_push(teco_interface_bytes2glyphs(pos));
+ teco_expressions_push(teco_interface_bytes2glyphs_absdot(pos));
}
/*$ \[rs]
@@ -267,8 +280,7 @@ teco_state_start_backslash(teco_machine_main_t *ctx, GError **error)
g_assert(*str != '\0');
gsize len = strlen(str);
- sptr_t pos = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0);
- teco_undo_int(teco_ranges[0].from) = teco_interface_bytes2glyphs(pos);
+ teco_undo_int(teco_ranges[0].from) = teco_current_doc_get_dot();
/*
* We can assume that `len` is already in glyphs,
* i.e. formatted numbers will never use multi-byte/Unicode characters.
@@ -276,6 +288,8 @@ teco_state_start_backslash(teco_machine_main_t *ctx, GError **error)
teco_undo_int(teco_ranges[0].to) = teco_ranges[0].from + len;
teco_undo_guint(teco_ranges_count) = 1;
+ teco_current_doc_set_dot(teco_ranges[0].to);
+
teco_interface_ssm(SCI_BEGINUNDOACTION, 0, 0);
teco_interface_ssm(SCI_ADDTEXT, len, (sptr_t)str);
teco_interface_ssm(SCI_ENDUNDOACTION, 0, 0);
@@ -563,6 +577,12 @@ teco_state_start_cmdline_push(teco_machine_main_t *ctx, GError **error)
* A undo action should always have been generated.
*/
undo__teco_interface_ssm(SCI_UNDO, 0, 0);
+
+ /*
+ * FIXME: If we would maintain dot in the command line,
+ * it could simply be copied.
+ */
+ teco_current_doc_set_dot(teco_interface_bytes2glyphs_rel(0, 0, teco_cmdline.pc));
}
static void
@@ -601,7 +621,8 @@ teco_state_start_cmdline_pop(teco_machine_main_t *ctx, GError **error)
* immediately after dot.
* - If <n> is -1, return the <code> of the character
* immediately preceding dot, ecetera.
- * - If <n> is omitted, the sign prefix is implied.
+ * - If <n> is omitted, 0 is implied.
+ * However \(lq-A\(rq is equivalent to \(lq-1A\(rq.
*
* If the position of the queried character is off-page,
* the command will return -1.
@@ -610,15 +631,22 @@ teco_state_start_cmdline_pop(teco_machine_main_t *ctx, GError **error)
* -2 is returned.
* Incomplete byte sequences are returned as -3.
*/
+/*
+ * NOTE: `A` without arguments is the Append I/O
+ * command in TECO-11. It doesn't make sense in our case.
+ */
static void
teco_state_start_get(teco_machine_main_t *ctx, GError **error)
{
- teco_int_t v;
- if (!teco_expressions_pop_num_calc(&v, teco_num_sign, error))
+ teco_int_t v = 0;
+
+ if (!teco_expressions_eval(FALSE, error))
+ return;
+ if ((teco_expressions_args() || teco_num_sign < 0) &&
+ !teco_expressions_pop_num_calc(&v, teco_num_sign, error))
return;
- sptr_t pos = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0);
- gssize get_pos = teco_interface_glyphs2bytes_relative(pos, v);
+ gssize get_pos = teco_interface_glyphs2bytes_reldot(v);
sptr_t len = teco_interface_ssm(SCI_GETLENGTH, 0, 0);
teco_expressions_push(get_pos < 0 || get_pos == len
@@ -732,7 +760,8 @@ teco_state_start_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
if (teco_is_noop(chr)) {
if (ctx->flags.modifier_at ||
(ctx->flags.mode == TECO_MODE_NORMAL && ctx->flags.modifier_colon)) {
- teco_error_modifier_set(error, chr);
+ gchar cmd[] = {chr, '\0'};
+ teco_error_modifier_set(error, cmd);
return NULL;
}
return &teco_state_start;
@@ -758,7 +787,8 @@ teco_state_start_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
case '0' ... '9':
if (ctx->flags.modifier_at ||
(ctx->flags.mode == TECO_MODE_NORMAL && ctx->flags.modifier_colon)) {
- teco_error_modifier_set(error, chr);
+ gchar cmd[] = {chr, '\0'};
+ teco_error_modifier_set(error, cmd);
return NULL;
}
if (ctx->flags.mode == TECO_MODE_NORMAL)
@@ -817,7 +847,7 @@ teco_state_start_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
case '|':
if (ctx->flags.modifier_at ||
(ctx->flags.mode == TECO_MODE_NORMAL && ctx->flags.modifier_colon)) {
- teco_error_modifier_set(error, '|');
+ teco_error_modifier_set(error, "|");
return NULL;
}
if (ctx->parent.must_undo)
@@ -829,29 +859,7 @@ teco_state_start_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
ctx->flags.mode = TECO_MODE_PARSE_ONLY_COND;
return &teco_state_start;
- case '\'':
- if (ctx->flags.modifier_at ||
- (ctx->flags.mode == TECO_MODE_NORMAL && ctx->flags.modifier_colon)) {
- teco_error_modifier_set(error, '\'');
- return NULL;
- }
- switch (ctx->flags.mode) {
- case TECO_MODE_PARSE_ONLY_COND:
- case TECO_MODE_PARSE_ONLY_COND_FORCE:
- if (!ctx->nest_level) {
- if (ctx->parent.must_undo)
- teco_undo_flags(ctx->flags);
- ctx->flags.mode = TECO_MODE_NORMAL;
- } else {
- if (ctx->parent.must_undo)
- teco_undo_gint(ctx->nest_level);
- ctx->nest_level--;
- }
- break;
- default:
- break;
- }
- return &teco_state_start;
+ case '\'': return teco_state_endcond(ctx, "'", error);
/*
* Word movement and deletion commands.
@@ -877,7 +885,7 @@ teco_state_start_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
*/
case '@':
if (ctx->flags.modifier_at) {
- teco_error_modifier_set(error, '@');
+ teco_error_modifier_set(error, "@");
return NULL;
}
/*
@@ -895,7 +903,7 @@ teco_state_start_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
if (ctx->flags.mode > TECO_MODE_NORMAL)
return &teco_state_start;
if (ctx->flags.modifier_colon >= 2) {
- teco_error_modifier_set(error, ':');
+ teco_error_modifier_set(error, ":");
return NULL;
}
if (ctx->parent.must_undo)
@@ -1078,6 +1086,10 @@ teco_state_fcommand_input(teco_machine_main_t *ctx, gunichar chr, GError **error
['|'] = {&teco_state_start, teco_state_fcommand_cond_else}
};
+ switch (chr) {
+ case '"': return teco_state_endcond(ctx, "F\"", error);
+ }
+
return teco_machine_main_transition_input(ctx, transitions, G_N_ELEMENTS(transitions),
teco_ascii_toupper(chr), error);
}
@@ -1297,6 +1309,35 @@ TECO_DEFINE_STATE_COMMAND(teco_state_condcommand,
.input_cb = (teco_state_input_cb_t)teco_state_condcommand_input
);
+static teco_state_t *
+teco_state_endcond(teco_machine_main_t *ctx, const gchar *cmd, GError **error)
+{
+ if (ctx->flags.modifier_at ||
+ (ctx->flags.mode == TECO_MODE_NORMAL && ctx->flags.modifier_colon)) {
+ teco_error_modifier_set(error, cmd);
+ return NULL;
+ }
+
+ switch (ctx->flags.mode) {
+ case TECO_MODE_PARSE_ONLY_COND:
+ case TECO_MODE_PARSE_ONLY_COND_FORCE:
+ if (!ctx->nest_level) {
+ if (ctx->parent.must_undo)
+ teco_undo_flags(ctx->flags);
+ ctx->flags.mode = TECO_MODE_NORMAL;
+ } else {
+ if (ctx->parent.must_undo)
+ teco_undo_gint(ctx->nest_level);
+ ctx->nest_level--;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return &teco_state_start;
+}
+
/*$ ^_ negate
* n^_ -> ~n -- Binary negation
*
@@ -1345,8 +1386,7 @@ teco_state_control_octal(teco_machine_main_t *ctx, GError **error)
{
teco_qreg_t *qreg = ctx->qreg_table_locals->radix;
assert(qreg != NULL);
- if (!qreg->vtable->undo_set_integer(qreg, error) ||
- !qreg->vtable->set_integer(qreg, 8, NULL))
+ if (!qreg->vtable->set_integer(qreg, 8, NULL))
return;
}
@@ -1358,8 +1398,7 @@ teco_state_control_decimal(teco_machine_main_t *ctx, GError **error)
{
teco_qreg_t *qreg = ctx->qreg_table_locals->radix;
assert(qreg != NULL);
- if (!qreg->vtable->undo_set_integer(qreg, error) ||
- !qreg->vtable->set_integer(qreg, 10, NULL))
+ if (!qreg->vtable->set_integer(qreg, 10, NULL))
return;
}
@@ -1390,8 +1429,7 @@ teco_state_control_radix(teco_machine_main_t *ctx, GError **error)
return;
teco_expressions_push(radix);
} else {
- if (!qreg->vtable->undo_set_integer(qreg, error) ||
- !qreg->vtable->set_integer(qreg, teco_expressions_pop_num(0), error))
+ if (!qreg->vtable->set_integer(qreg, teco_expressions_pop_num(0), error))
return;
}
}
@@ -1444,14 +1482,9 @@ teco_state_control_glyphs2bytes(teco_machine_main_t *ctx, GError **error)
res = teco_interface_ssm(colon_modified ? SCI_GETLENGTH : SCI_GETCURRENTPOS, 0, 0);
} else {
teco_int_t pos = teco_expressions_pop_num(0);
- if (colon_modified) {
- /* teco_interface_bytes2glyphs() does not check addresses */
- res = 0 <= pos && pos <= teco_interface_ssm(SCI_GETLENGTH, 0, 0)
- ? teco_interface_bytes2glyphs(pos) : -1;
- } else {
- /* negative values for invalid indexes are passed down. */
- res = teco_interface_glyphs2bytes(pos);
- }
+ /* negative values for invalid indexes are passed down. */
+ res = colon_modified ? teco_interface_bytes2glyphs_absdot(pos)
+ : teco_interface_glyphs2bytes_absdot(pos);
}
teco_expressions_push(res);
@@ -2671,7 +2704,7 @@ teco_state_ecommand_encoding(teco_machine_main_t *ctx, GError **error)
teco_int_t dot_glyphs = 0;
if (colon_modified) {
sptr_t dot_bytes = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0);
- dot_glyphs = teco_interface_bytes2glyphs(dot_bytes);
+ dot_glyphs = teco_current_doc_get_dot();
/*
* Convert buffer to new codepage.
@@ -2719,7 +2752,7 @@ teco_state_ecommand_encoding(teco_machine_main_t *ctx, GError **error)
teco_interface_ssm(SCI_SETCODEPAGE, SC_CP_UTF8, 0);
/*
* UTF-8 documents strictly require the line character index.
- * See teco_view_glyphs2bytes() and teco_view_bytes2glyphs().
+ * See teco_view_glyphs2bytes_unicode() and teco_view_bytes2glyphs_unicode().
*/
g_assert(!(teco_interface_ssm(SCI_GETLINECHARACTERINDEX, 0, 0)
& SC_LINECHARACTERINDEX_UTF32));
@@ -2763,13 +2796,18 @@ teco_state_ecommand_encoding(teco_machine_main_t *ctx, GError **error)
teco_interface_ssm(SCI_SETCODEPAGE, 0, 0);
}
- if (colon_modified)
+ if (colon_modified) {
/*
* Only now, it will be safe to recalculate dot in the new encoding.
* If the new codepage is UTF-8, the line character index will be
* ready only now.
*/
- teco_interface_ssm(SCI_GOTOPOS, teco_interface_glyphs2bytes(dot_glyphs), 0);
+ teco_interface_ssm(SCI_GOTOPOS, teco_interface_glyphs2bytes_rel(0, 0, dot_glyphs), 0);
+ } else {
+ /* recalculate dot */
+ sptr_t pos = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0);
+ teco_current_doc_set_dot(teco_interface_bytes2glyphs_rel(0, 0, pos));
+ }
}
/*$ EO version
@@ -2869,6 +2907,7 @@ teco_state_ecommand_input(teco_machine_main_t *ctx, gunichar chr, GError **error
/*
* Simple Transitions
*/
+ ['*'] = {&teco_state_save_cmdline},
['%'] = {&teco_state_epctcommand,
.modifier_at = TRUE},
['B'] = {&teco_state_edit_file,
@@ -2928,8 +2967,7 @@ teco_state_insert_initial(teco_machine_main_t *ctx, GError **error)
if (ctx->flags.mode > TECO_MODE_NORMAL)
return TRUE;
- sptr_t pos = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0);
- teco_undo_int(teco_ranges[0].from) = teco_interface_bytes2glyphs(pos);
+ teco_undo_int(teco_ranges[0].from) = teco_current_doc_get_dot();
teco_undo_guint(teco_ranges_count) = 1;
/*
@@ -2942,8 +2980,10 @@ teco_state_insert_initial(teco_machine_main_t *ctx, GError **error)
if (!teco_expressions_eval(FALSE, error))
return FALSE;
guint args = teco_expressions_args();
- if (!args)
+ if (!args) {
+ teco_undo_int(teco_ranges[0].to) = teco_ranges[0].from;
return TRUE;
+ }
if (teco_interface_ssm(SCI_GETCODEPAGE, 0, 0) == SC_CP_UTF8) {
/* detect possible errors before introducing side effects */
@@ -2986,6 +3026,9 @@ teco_state_insert_initial(teco_machine_main_t *ctx, GError **error)
for (gint i = 0; i < args; i++)
teco_expressions_pop_num(0);
+ /* this way we remember how many numeric arguments were given */
+ teco_undo_int(teco_ranges[0].to) = teco_ranges[0].from + args;
+
return TRUE;
}
@@ -3013,8 +3056,15 @@ teco_state_insert_done(teco_machine_main_t *ctx, teco_string_t str, GError **err
if (ctx->flags.mode > TECO_MODE_NORMAL)
return &teco_state_start;
+ /*
+ * NOTE: teco_interface_bytes2glyphs_reldot() would count relative to
+ * dot, which is still invalid.
+ */
sptr_t pos = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0);
- teco_undo_int(teco_ranges[0].to) = teco_interface_bytes2glyphs(pos);
+ teco_int_t to = teco_interface_bytes2glyphs_rel(teco_ranges[0].to, pos-str.len, str.len);
+ teco_undo_int(teco_ranges[0].to) = to;
+ teco_current_doc_set_dot(to);
+
return &teco_state_start;
}
@@ -3053,12 +3103,18 @@ teco_state_insert_indent_initial(teco_machine_main_t *ctx, GError **error)
teco_interface_ssm(SCI_BEGINUNDOACTION, 0, 0);
if (teco_interface_ssm(SCI_GETUSETABS, 0, 0)) {
teco_interface_ssm(SCI_ADDTEXT, 1, (sptr_t)"\t");
+
+ /* teco_state_insert_done() expects this */
+ teco_undo_int(teco_ranges[0].to)++;
} else {
gint len = teco_interface_ssm(SCI_GETTABWIDTH, 0, 0);
len -= teco_interface_ssm(SCI_GETCOLUMN,
teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0), 0) % len;
+ /* teco_state_insert_done() expects this */
+ teco_undo_int(teco_ranges[0].to) += len;
+
gchar space = ' ';
while (len-- > 0)
teco_interface_ssm(SCI_ADDTEXT, 1, (sptr_t)&space);