aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorRobin Haberkorn <robin.haberkorn@googlemail.com>2024-09-11 16:01:51 +0200
committerRobin Haberkorn <robin.haberkorn@googlemail.com>2024-09-11 16:14:27 +0200
commitcc63f3b25cd449573fe9b6b7d0c88f5dd0ed2f4d (patch)
tree1be1dc8d53e133ec1e0e1610e6248dc5619b2ec5
parent2a050759ab621b87d0782cc8235907a1757b46cc (diff)
downloadsciteco-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.c58
-rw-r--r--src/file-utils.c4
-rw-r--r--src/glob.c2
-rw-r--r--src/glob.h15
-rw-r--r--src/ring.c2
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);
diff --git a/src/glob.c b/src/glob.c
index 9aa499d..0374d7c 100644
--- a/src/glob.c
+++ b/src/glob.c
@@ -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
);
diff --git a/src/glob.h b/src/glob.h
index 75b066c..8f03d38 100644
--- a/src/glob.h
+++ b/src/glob.h
@@ -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
*/
diff --git a/src/ring.c b/src/ring.c
index bc30e8e..5a9e74e 100644
--- a/src/ring.c
+++ b/src/ring.c
@@ -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
);