aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/cmdline.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmdline.c')
-rw-r--r--src/cmdline.c116
1 files changed, 72 insertions, 44 deletions
diff --git a/src/cmdline.c b/src/cmdline.c
index e1a4628..b944b5e 100644
--- a/src/cmdline.c
+++ b/src/cmdline.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
@@ -99,6 +99,14 @@ teco_cmdline_init(void)
teco_cmdline_ssm(SCI_SETTABDRAWMODE, SCTD_CONTROLCHAR, 0);
/*
+ * FIXME: This works around a problem where the caret is not
+ * scrolled in multi-line mode after it wraps at the end of the
+ * line. You cannot scroll the command-line with the mouse, so the
+ * user won't see any difference.
+ */
+ teco_cmdline_ssm(SCI_SETENDATLASTLINE, FALSE, 0);
+
+ /*
* FIXME: Something resets the margin text, so we have to set it last.
*/
teco_cmdline_ssm(SCI_MARGINSETTEXT, 0, (sptr_t)"*");
@@ -128,7 +136,7 @@ teco_cmdline_insert(const gchar *data, gsize len, GError **error)
gsize macro_len = teco_cmdline_ssm(SCI_GETLENGTH, 0, 0);
if (len <= macro_len - effective_len &&
- !teco_string_cmp(&src, macro + effective_len, len)) {
+ !teco_string_cmp(src, macro + effective_len, len)) {
/* extend effective command line from rubbed out part */
teco_cmdline_ssm(SCI_GOTOPOS, effective_len+len, 0);
} else {
@@ -162,19 +170,39 @@ teco_cmdline_insert(const gchar *data, gsize len, GError **error)
* Result of command line replacement (}):
* Exchange command lines
*/
+ g_clear_error(&tmp_error);
+
teco_qreg_t *cmdline_reg = teco_qreg_table_find(&teco_qreg_table_globals, "\e", 1);
g_auto(teco_string_t) new_cmdline = {NULL, 0};
if (!cmdline_reg->vtable->get_string(cmdline_reg, &new_cmdline.data, &new_cmdline.len,
- NULL, error))
+ NULL, &tmp_error)) {
+ teco_error_add_frame_toplevel();
+ teco_error_display_short(tmp_error);
+ g_propagate_error(error, g_steal_pointer(&tmp_error));
+ return FALSE;
+ }
+
+ /*
+ * SciTECO code must always be UTF-8, but you can smuggle arbitrary bytes
+ * into the "\e" register.
+ * This would be cumbersome to test for in teco_state_start_cmdline_pop().
+ */
+ if (!teco_string_validate_utf8(new_cmdline)) {
+ g_set_error_literal(&tmp_error, TECO_ERROR, TECO_ERROR_CODEPOINT,
+ "Invalid UTF-8 byte sequence in command-line replacement");
+ teco_error_add_frame_toplevel();
+ teco_error_display_short(tmp_error);
+ g_propagate_error(error, g_steal_pointer(&tmp_error));
return FALSE;
+ }
/*
* Search for first differing character in old and
* new command line. This avoids unnecessary rubouts
* and insertions when the command line is updated.
*/
- teco_cmdline.pc = teco_string_diff(&new_cmdline, macro, effective_len);
+ teco_cmdline.pc = teco_string_diff(new_cmdline, macro, effective_len);
teco_undo_pop(teco_cmdline.pc);
@@ -229,7 +257,7 @@ teco_cmdline_insert(const gchar *data, gsize len, GError **error)
}
}
- /* error is handled in teco_cmdline_keypress_c() */
+ /* error is handled in teco_cmdline_keypress() */
g_propagate_error(error, g_steal_pointer(&tmp_error));
return FALSE;
}
@@ -275,10 +303,9 @@ teco_cmdline_rubin(GError **error)
gboolean
teco_cmdline_keypress(const gchar *data, gsize len, GError **error)
{
- const teco_string_t str = {(gchar *)data, len};
teco_machine_t *machine = &teco_cmdline.machine.parent;
- if (!teco_string_validate_utf8(&str)) {
+ if (!teco_string_validate_utf8((teco_string_t){(gchar *)data, len})) {
g_set_error_literal(error, TECO_ERROR, TECO_ERROR_CODEPOINT,
"Invalid UTF-8 sequence");
return FALSE;
@@ -694,14 +721,14 @@ teco_state_stringbuilding_start_process_edit_cmd(teco_machine_stringbuilding_t *
/* reinsert word chars */
while (ctx->parent.current == current &&
teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0) < macro_len &&
- teco_string_contains(&wchars, macro[teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0)]))
+ teco_string_contains(wchars, macro[teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0)]))
if (!teco_cmdline_rubin(error))
return FALSE;
/* reinsert non-word chars */
while (ctx->parent.current == current &&
teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0) < macro_len &&
- !teco_string_contains(&wchars, macro[teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0)]))
+ !teco_string_contains(wchars, macro[teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0)]))
if (!teco_cmdline_rubin(error))
return FALSE;
@@ -715,7 +742,7 @@ teco_state_stringbuilding_start_process_edit_cmd(teco_machine_stringbuilding_t *
* a result string even in parse-only mode.
*/
if (ctx->result && ctx->result->len > 0) {
- gboolean is_wordchar = teco_string_contains(&wchars, macro[teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0)-1]);
+ gboolean is_wordchar = teco_string_contains(wchars, macro[teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0)-1]);
teco_cmdline_rubout();
if (ctx->parent.current != current) {
/* rub out string building command */
@@ -731,13 +758,13 @@ teco_state_stringbuilding_start_process_edit_cmd(teco_machine_stringbuilding_t *
*/
if (!is_wordchar) {
while (ctx->result->len > 0 &&
- !teco_string_contains(&wchars, macro[teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0)-1]))
+ !teco_string_contains(wchars, macro[teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0)-1]))
teco_cmdline_rubout();
}
/* rubout word chars */
while (ctx->result->len > 0 &&
- teco_string_contains(&wchars, macro[teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0)-1]))
+ teco_string_contains(wchars, macro[teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0)-1]))
teco_cmdline_rubout();
return TRUE;
@@ -792,7 +819,7 @@ teco_state_stringbuilding_start_process_edit_cmd(teco_machine_stringbuilding_t *
return TRUE;
}
- const gchar *filename = teco_string_last_occurrence(ctx->result,
+ const gchar *filename = teco_string_last_occurrence(*ctx->result,
TECO_DEFAULT_BREAK_CHARS);
g_auto(teco_string_t) new_chars, new_chars_escaped;
gboolean unambiguous = teco_file_auto_complete(filename, G_FILE_TEST_EXISTS, &new_chars);
@@ -823,11 +850,11 @@ teco_state_stringbuilding_start_process_edit_cmd(teco_machine_stringbuilding_t *
}
gboolean
-teco_state_stringbuilding_insert_completion(teco_machine_stringbuilding_t *ctx, const teco_string_t *str, GError **error)
+teco_state_stringbuilding_insert_completion(teco_machine_stringbuilding_t *ctx, teco_string_t str, GError **error)
{
g_auto(teco_string_t) str_escaped;
- teco_machine_stringbuilding_escape(ctx, str->data, str->len, &str_escaped);
- if (!str->len || !G_IS_DIR_SEPARATOR(str->data[str->len-1]))
+ teco_machine_stringbuilding_escape(ctx, str.data, str.len, &str_escaped);
+ if (!str.len || !G_IS_DIR_SEPARATOR(str.data[str.len-1]))
teco_string_append_c(&str_escaped, ' ');
return teco_cmdline_insert(str_escaped.data, str_escaped.len, error);
}
@@ -872,7 +899,7 @@ teco_state_expectstring_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_
}
gboolean
-teco_state_expectstring_insert_completion(teco_machine_main_t *ctx, const teco_string_t *str, GError **error)
+teco_state_expectstring_insert_completion(teco_machine_main_t *ctx, teco_string_t str, GError **error)
{
teco_machine_stringbuilding_t *stringbuilding_ctx = &ctx->expectstring.machine;
teco_state_t *stringbuilding_current = stringbuilding_ctx->parent.current;
@@ -981,7 +1008,7 @@ teco_state_expectfile_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t
return TRUE;
}
- if (teco_string_contains(&ctx->expectstring.string, '\0'))
+ if (teco_string_contains(ctx->expectstring.string, '\0'))
/* null-byte not allowed in file names */
return TRUE;
@@ -1000,13 +1027,13 @@ teco_state_expectfile_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t
}
gboolean
-teco_state_expectfile_insert_completion(teco_machine_main_t *ctx, const teco_string_t *str, GError **error)
+teco_state_expectfile_insert_completion(teco_machine_main_t *ctx, teco_string_t str, GError **error)
{
teco_machine_stringbuilding_t *stringbuilding_ctx = &ctx->expectstring.machine;
g_auto(teco_string_t) str_escaped;
- teco_machine_stringbuilding_escape(stringbuilding_ctx, str->data, str->len, &str_escaped);
- if ((!str->len || !G_IS_DIR_SEPARATOR(str->data[str->len-1])) &&
+ teco_machine_stringbuilding_escape(stringbuilding_ctx, str.data, str.len, &str_escaped);
+ if ((!str.len || !G_IS_DIR_SEPARATOR(str.data[str.len-1])) &&
ctx->expectstring.nesting == 1)
teco_string_append_wc(&str_escaped,
ctx->expectstring.machine.escape_char == '{' ? '}' : ctx->expectstring.machine.escape_char);
@@ -1037,7 +1064,7 @@ teco_state_expectglob_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t
return TRUE;
}
- if (teco_string_contains(&ctx->expectstring.string, '\0'))
+ if (teco_string_contains(ctx->expectstring.string, '\0'))
/* null-byte not allowed in file names */
return TRUE;
@@ -1068,14 +1095,14 @@ teco_state_expectglob_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t
}
gboolean
-teco_state_expectglob_insert_completion(teco_machine_main_t *ctx, const teco_string_t *str, GError **error)
+teco_state_expectglob_insert_completion(teco_machine_main_t *ctx, teco_string_t str, GError **error)
{
teco_machine_stringbuilding_t *stringbuilding_ctx = &ctx->expectstring.machine;
- g_autofree gchar *pattern_escaped = teco_globber_escape_pattern(str->data);
+ g_autofree gchar *pattern_escaped = teco_globber_escape_pattern(str.data);
g_auto(teco_string_t) str_escaped;
teco_machine_stringbuilding_escape(stringbuilding_ctx, pattern_escaped, strlen(pattern_escaped), &str_escaped);
- if ((!str->len || !G_IS_DIR_SEPARATOR(str->data[str->len-1])) &&
+ if ((!str.len || !G_IS_DIR_SEPARATOR(str.data[str.len-1])) &&
ctx->expectstring.nesting == 1)
teco_string_append_wc(&str_escaped,
ctx->expectstring.machine.escape_char == '{' ? '}' : ctx->expectstring.machine.escape_char);
@@ -1106,7 +1133,7 @@ teco_state_expectdir_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *
return TRUE;
}
- if (teco_string_contains(&ctx->expectstring.string, '\0'))
+ if (teco_string_contains(ctx->expectstring.string, '\0'))
/* null-byte not allowed in file names */
return TRUE;
@@ -1126,7 +1153,7 @@ teco_state_expectdir_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *
}
gboolean
-teco_state_expectdir_insert_completion(teco_machine_main_t *ctx, const teco_string_t *str, GError **error)
+teco_state_expectdir_insert_completion(teco_machine_main_t *ctx, teco_string_t str, GError **error)
{
teco_machine_stringbuilding_t *stringbuilding_ctx = &ctx->expectstring.machine;
@@ -1134,7 +1161,7 @@ teco_state_expectdir_insert_completion(teco_machine_main_t *ctx, const teco_stri
* FIXME: We might terminate the command in case of leaf directories.
*/
g_auto(teco_string_t) str_escaped;
- teco_machine_stringbuilding_escape(stringbuilding_ctx, str->data, str->len, &str_escaped);
+ teco_machine_stringbuilding_escape(stringbuilding_ctx, str.data, str.len, &str_escaped);
return teco_cmdline_insert(str_escaped.data, str_escaped.len, error);
}
@@ -1151,7 +1178,7 @@ teco_state_expectqreg_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t
}
gboolean
-teco_state_expectqreg_insert_completion(teco_machine_main_t *ctx, const teco_string_t *str, GError **error)
+teco_state_expectqreg_insert_completion(teco_machine_main_t *ctx, teco_string_t str, GError **error)
{
g_assert(ctx->expectqreg != NULL);
/*
@@ -1202,9 +1229,9 @@ teco_state_qregspec_process_edit_cmd(teco_machine_qregspec_t *ctx, teco_machine_
}
gboolean
-teco_state_qregspec_insert_completion(teco_machine_qregspec_t *ctx, const teco_string_t *str, GError **error)
+teco_state_qregspec_insert_completion(teco_machine_qregspec_t *ctx, teco_string_t str, GError **error)
{
- return teco_cmdline_insert(str->data, str->len, error);
+ return teco_cmdline_insert(str.data, str.len, error);
}
gboolean
@@ -1248,12 +1275,12 @@ teco_state_qregspec_string_process_edit_cmd(teco_machine_qregspec_t *ctx, teco_m
}
gboolean
-teco_state_qregspec_string_insert_completion(teco_machine_qregspec_t *ctx, const teco_string_t *str, GError **error)
+teco_state_qregspec_string_insert_completion(teco_machine_qregspec_t *ctx, teco_string_t str, GError **error)
{
teco_machine_stringbuilding_t *stringbuilding_ctx = teco_machine_qregspec_get_stringbuilding(ctx);
g_auto(teco_string_t) str_escaped;
- teco_machine_stringbuilding_escape(stringbuilding_ctx, str->data, str->len, &str_escaped);
+ teco_machine_stringbuilding_escape(stringbuilding_ctx, str.data, str.len, &str_escaped);
teco_string_append_c(&str_escaped, ']');
return teco_cmdline_insert(str_escaped.data, str_escaped.len, error);
}
@@ -1288,7 +1315,7 @@ teco_state_execute_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *pa
return TRUE;
}
- const gchar *filename = teco_string_last_occurrence(&ctx->expectstring.string,
+ const gchar *filename = teco_string_last_occurrence(ctx->expectstring.string,
TECO_DEFAULT_BREAK_CHARS);
g_auto(teco_string_t) new_chars, new_chars_escaped;
gboolean unambiguous = teco_file_auto_complete(filename, G_FILE_TEST_EXISTS, &new_chars);
@@ -1327,7 +1354,7 @@ teco_state_scintilla_symbols_process_edit_cmd(teco_machine_main_t *ctx, teco_mac
return TRUE;
}
- const gchar *symbol = teco_string_last_occurrence(&ctx->expectstring.string, ",");
+ const gchar *symbol = teco_string_last_occurrence(ctx->expectstring.string, ",");
teco_symbol_list_t *list = symbol == ctx->expectstring.string.data
? &teco_symbol_list_scintilla
: &teco_symbol_list_scilexer;
@@ -1349,12 +1376,12 @@ teco_state_scintilla_symbols_process_edit_cmd(teco_machine_main_t *ctx, teco_mac
}
gboolean
-teco_state_scintilla_symbols_insert_completion(teco_machine_main_t *ctx, const teco_string_t *str, GError **error)
+teco_state_scintilla_symbols_insert_completion(teco_machine_main_t *ctx, teco_string_t str, GError **error)
{
teco_machine_stringbuilding_t *stringbuilding_ctx = &ctx->expectstring.machine;
g_auto(teco_string_t) str_escaped;
- teco_machine_stringbuilding_escape(stringbuilding_ctx, str->data, str->len, &str_escaped);
+ teco_machine_stringbuilding_escape(stringbuilding_ctx, str.data, str.len, &str_escaped);
teco_string_append_c(&str_escaped, ',');
return teco_cmdline_insert(str_escaped.data, str_escaped.len, error);
}
@@ -1384,7 +1411,7 @@ teco_state_goto_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *paren
}
teco_string_t label = ctx->expectstring.string;
- gint i = teco_string_rindex(&label, ',');
+ gint i = teco_string_rindex(label, ',');
if (i >= 0) {
label.data += i+1;
label.len -= i+1;
@@ -1407,12 +1434,12 @@ teco_state_goto_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *paren
}
gboolean
-teco_state_goto_insert_completion(teco_machine_main_t *ctx, const teco_string_t *str, GError **error)
+teco_state_goto_insert_completion(teco_machine_main_t *ctx, teco_string_t str, GError **error)
{
teco_machine_stringbuilding_t *stringbuilding_ctx = &ctx->expectstring.machine;
g_auto(teco_string_t) str_escaped;
- teco_machine_stringbuilding_escape(stringbuilding_ctx, str->data, str->len, &str_escaped);
+ teco_machine_stringbuilding_escape(stringbuilding_ctx, str.data, str.len, &str_escaped);
/*
* FIXME: This does not escape `,`. Cannot be escaped via ^Q currently?
*/
@@ -1444,7 +1471,7 @@ teco_state_help_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *paren
return TRUE;
}
- if (teco_string_contains(&ctx->expectstring.string, '\0'))
+ if (teco_string_contains(ctx->expectstring.string, '\0'))
/* help term must not contain null-byte */
return TRUE;
@@ -1463,12 +1490,12 @@ teco_state_help_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *paren
}
gboolean
-teco_state_help_insert_completion(teco_machine_main_t *ctx, const teco_string_t *str, GError **error)
+teco_state_help_insert_completion(teco_machine_main_t *ctx, teco_string_t str, GError **error)
{
teco_machine_stringbuilding_t *stringbuilding_ctx = &ctx->expectstring.machine;
g_auto(teco_string_t) str_escaped;
- teco_machine_stringbuilding_escape(stringbuilding_ctx, str->data, str->len, &str_escaped);
+ teco_machine_stringbuilding_escape(stringbuilding_ctx, str.data, str.len, &str_escaped);
if (ctx->expectstring.nesting == 1)
teco_string_append_wc(&str_escaped,
ctx->expectstring.machine.escape_char == '{' ? '}' : ctx->expectstring.machine.escape_char);
@@ -1504,5 +1531,6 @@ teco_state_save_cmdline_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg
* Q-Register <q>.
*/
TECO_DEFINE_STATE_EXPECTQREG(teco_state_save_cmdline,
- .expectqreg.type = TECO_QREG_OPTIONAL_INIT
+ .expectqreg.type = TECO_QREG_OPTIONAL_INIT,
+ .expectqreg.got_register_cb = teco_state_save_cmdline_got_register
);