diff options
author | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2024-09-11 16:01:51 +0200 |
---|---|---|
committer | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2024-09-11 16:14:27 +0200 |
commit | cc63f3b25cd449573fe9b6b7d0c88f5dd0ed2f4d (patch) | |
tree | 1be1dc8d53e133ec1e0e1610e6248dc5619b2ec5 | |
parent | 2a050759ab621b87d0782cc8235907a1757b46cc (diff) | |
download | sciteco-cc63f3b25cd449573fe9b6b7d0c88f5dd0ed2f4d.tar.gz |
improved file name autocompletion
* pressing ^W in FG now deletes the entire directory component as in EB
* commands without glob patterns (eg. EW) can now autocomplete file names containing
glob patterns
* When the autocompletion contains a glob character in commands accepting
glob patterns like EB or EN, we now escape the glob pattern.
This already helps if the remaining file name can be autocompleted in one go.
Unfortunately, this is still insufficient if we can only partially complete
and the partial completion contains glob characters.
For instance, if there are 2 files: `file?.txt` and `file?.foo`,
completing after `f` will insert `ile[?].`.
The second try to press Tab will already do nothing.
To fully support these cases, we need a version of teco_file_auto_complete()
accepting glob patterns.
Perhaps we can simply append `*` to the given glob pattern.
-rw-r--r-- | src/cmdline.c | 58 | ||||
-rw-r--r-- | src/file-utils.c | 4 | ||||
-rw-r--r-- | src/glob.c | 2 | ||||
-rw-r--r-- | src/glob.h | 15 | ||||
-rw-r--r-- | src/ring.c | 2 |
5 files changed, 74 insertions, 7 deletions
diff --git a/src/cmdline.c b/src/cmdline.c index be7a5b1..2236872 100644 --- a/src/cmdline.c +++ b/src/cmdline.c @@ -52,6 +52,7 @@ #include "eol.h" #include "error.h" #include "qreg.h" +#include "glob.h" #include "cmdline.h" #if defined(HAVE_MALLOC_TRIM) && !defined(HAVE_DECL_MALLOC_TRIM) @@ -762,6 +763,60 @@ teco_state_expectfile_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t } gboolean +teco_state_expectglob_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx, gunichar key, GError **error) +{ + teco_machine_stringbuilding_t *stringbuilding_ctx = &ctx->expectstring.machine; + teco_state_t *stringbuilding_current = stringbuilding_ctx->parent.current; + + /* + * NOTE: We don't just define teco_state_stringbuilding_start_process_edit_cmd(), + * as it would be hard to subclass/overwrite for different main machine states. + */ + if (!stringbuilding_current->is_start) + return stringbuilding_current->process_edit_cmd_cb(&stringbuilding_ctx->parent, &ctx->parent, key, error); + + switch (key) { + case '\t': { /* autocomplete file name */ + if (teco_cmdline.modifier_enabled) + break; + + if (teco_interface_popup_is_shown()) { + /* cycle through popup pages */ + teco_interface_popup_show(); + return TRUE; + } + + if (teco_string_contains(&ctx->expectstring.string, '\0')) + /* null-byte not allowed in file names */ + return TRUE; + + /* + * We do not support autocompleting glob patterns. + * + * FIXME: What if the last autocompletion inserted escaped glob + * characters? + * Perhaps teco_file_auto_complete() should natively support glob patterns. + */ + if (teco_globber_is_pattern(ctx->expectstring.string.data)) + return TRUE; + + g_auto(teco_string_t) new_chars, new_chars_escaped; + gboolean unambiguous = teco_file_auto_complete(ctx->expectstring.string.data, G_FILE_TEST_EXISTS, &new_chars); + g_autofree gchar *pattern_escaped = teco_globber_escape_pattern(new_chars.data); + teco_machine_stringbuilding_escape(stringbuilding_ctx, pattern_escaped, strlen(pattern_escaped), &new_chars_escaped); + if (unambiguous && ctx->expectstring.nesting == 1) + teco_string_append_wc(&new_chars_escaped, + ctx->expectstring.machine.escape_char == '{' ? '}' : ctx->expectstring.machine.escape_char); + + return teco_cmdline_insert(new_chars_escaped.data, new_chars_escaped.len, error); + } + } + + /* ^W should behave like in commands accepting files */ + return teco_state_expectfile_process_edit_cmd(ctx, parent_ctx, key, error); +} + +gboolean teco_state_expectdir_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx, gunichar key, GError **error) { teco_machine_stringbuilding_t *stringbuilding_ctx = &ctx->expectstring.machine; @@ -800,7 +855,8 @@ teco_state_expectdir_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t * } } - return stringbuilding_current->process_edit_cmd_cb(&stringbuilding_ctx->parent, &ctx->parent, key, error); + /* ^W should behave like in commands accepting files */ + return teco_state_expectfile_process_edit_cmd(ctx, parent_ctx, key, error); } gboolean diff --git a/src/file-utils.c b/src/file-utils.c index 0909d7a..3f8f721 100644 --- a/src/file-utils.c +++ b/src/file-utils.c @@ -37,7 +37,6 @@ #include "sciteco.h" #include "qreg.h" -#include "glob.h" #include "interface.h" #include "string-utils.h" #include "file-utils.h" @@ -237,9 +236,6 @@ teco_file_auto_complete(const gchar *filename, GFileTest file_test, teco_string_ { memset(insert, 0, sizeof(*insert)); - if (teco_globber_is_pattern(filename)) - return FALSE; - g_autofree gchar *filename_expanded = teco_file_expand_path(filename); gsize filename_len = strlen(filename_expanded); @@ -452,7 +452,7 @@ teco_state_glob_pattern_done(teco_machine_main_t *ctx, const teco_string_t *str, * when they should be in a register, the user will * have to edit that register anyway. */ -TECO_DEFINE_STATE_EXPECTFILE(teco_state_glob_pattern, +TECO_DEFINE_STATE_EXPECTGLOB(teco_state_glob_pattern, .expectstring.last = FALSE ); @@ -46,6 +46,21 @@ teco_globber_is_pattern(const gchar *str) gchar *teco_globber_escape_pattern(const gchar *pattern); GRegex *teco_globber_compile_pattern(const gchar *pattern); +/* in cmdline.c */ +gboolean teco_state_expectglob_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx, gunichar key, GError **error); + +/** + * @interface TECO_DEFINE_STATE_EXPECTGLOB + * @implements TECO_DEFINE_STATE_EXPECTFILE + * @ingroup states + */ +#define TECO_DEFINE_STATE_EXPECTGLOB(NAME, ...) \ + TECO_DEFINE_STATE_EXPECTFILE(NAME, \ + .process_edit_cmd_cb = (teco_state_process_edit_cmd_cb_t) \ + teco_state_expectglob_process_edit_cmd, \ + ##__VA_ARGS__ \ + ) + /* * Command states */ @@ -512,7 +512,7 @@ teco_state_edit_file_done(teco_machine_main_t *ctx, const teco_string_t *str, GE * A value of 1 denotes the first buffer, 2 the second, * ecetera. */ -TECO_DEFINE_STATE_EXPECTFILE(teco_state_edit_file, +TECO_DEFINE_STATE_EXPECTGLOB(teco_state_edit_file, .initial_cb = (teco_state_initial_cb_t)teco_state_edit_file_initial ); |