diff options
| author | Mitja Felicijan <mitja.felicijan@gmail.com> | 2026-01-21 22:52:54 +0100 |
|---|---|---|
| committer | Mitja Felicijan <mitja.felicijan@gmail.com> | 2026-01-21 22:52:54 +0100 |
| commit | dcacc00e3750300617ba6e16eb346713f91a783a (patch) | |
| tree | 38e2d4fb5ed9d119711d4295c6eda4b014af73fd /examples/dte/completion.c | |
| parent | 58dac10aeb8f5a041c46bddbeaf4c7966a99b998 (diff) | |
| download | crep-dcacc00e3750300617ba6e16eb346713f91a783a.tar.gz | |
Remove testing data
Diffstat (limited to 'examples/dte/completion.c')
| -rw-r--r-- | examples/dte/completion.c | 879 |
1 files changed, 0 insertions, 879 deletions
diff --git a/examples/dte/completion.c b/examples/dte/completion.c deleted file mode 100644 index 89e8c8c..0000000 --- a/examples/dte/completion.c +++ /dev/null @@ -1,879 +0,0 @@ -#include <fcntl.h> -#include <stdbool.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> -#include <unistd.h> -#include "completion.h" -#include "bind.h" -#include "command/alias.h" -#include "command/args.h" -#include "command/parse.h" -#include "command/run.h" -#include "command/serialize.h" -#include "commands.h" -#include "compiler.h" -#include "config.h" -#include "filetype.h" -#include "options.h" -#include "show.h" -#include "syntax/color.h" -#include "tag.h" -#include "terminal/cursor.h" -#include "terminal/key.h" -#include "terminal/style.h" -#include "util/arith.h" -#include "util/array.h" -#include "util/ascii.h" -#include "util/bsearch.h" -#include "util/debug.h" -#include "util/intmap.h" -#include "util/log.h" -#include "util/numtostr.h" -#include "util/path.h" -#include "util/str-util.h" -#include "util/string-view.h" -#include "util/string.h" -#include "util/xdirent.h" -#include "util/xmalloc.h" -#include "vars.h" - -extern char **environ; - -typedef enum { - COLLECT_ALL, // (directories and files) - COLLECT_EXECUTABLES, // (directories and executable files) - COLLECT_DIRS_ONLY, -} FileCollectionType; - -static bool is_executable(int dir_fd, const char *filename) -{ - return faccessat(dir_fd, filename, X_OK, 0) == 0; -} - -static bool do_collect_files ( - PointerArray *array, - const char *dirname, - const char *dirprefix, - const char *fileprefix, - FileCollectionType type -) { - DIR *const dir = xopendir(dirname); - if (!dir) { - return false; - } - - const int dir_fd = dirfd(dir); - if (unlikely(dir_fd < 0)) { - LOG_ERRNO("dirfd"); - xclosedir(dir); - return false; - } - - size_t dlen = strlen(dirprefix); - size_t flen = strlen(fileprefix); - const struct dirent *de; - - while ((de = xreaddir(dir))) { - const char *name = de->d_name; - if (streq(name, ".") || streq(name, "..") || unlikely(streq(name, ""))) { - continue; - } - - // TODO: add a global option to allow dotfiles to be included - // even when there's no prefix - if (flen ? strncmp(name, fileprefix, flen) : name[0] == '.') { - continue; - } - - struct stat st; - if (fstatat(dir_fd, name, &st, AT_SYMLINK_NOFOLLOW)) { - continue; - } - - bool is_dir = S_ISDIR(st.st_mode); - if (S_ISLNK(st.st_mode)) { - if (!fstatat(dir_fd, name, &st, 0)) { - is_dir = S_ISDIR(st.st_mode); - } - } - - if (!is_dir) { - switch (type) { - case COLLECT_DIRS_ONLY: - continue; - case COLLECT_ALL: - break; - case COLLECT_EXECUTABLES: - if (!is_executable(dir_fd, name)) { - continue; - } - if (!dlen) { - dirprefix = "./"; - dlen = 2; - } - break; - default: - BUG("unhandled FileCollectionType value"); - } - } - - ptr_array_append(array, path_joinx(dirprefix, name, is_dir)); - } - - xclosedir(dir); - return true; -} - -static void collect_files(EditorState *e, CompletionState *cs, FileCollectionType type) -{ - StringView esc = cs->escaped; - if (strview_has_prefix(&esc, "~/")) { - CommandRunner runner = cmdrunner_for_mode(e, INPUT_NORMAL, false); - char *str = parse_command_arg(&runner, esc.data, esc.length, false); - const char *slash = strrchr(str, '/'); - BUG_ON(!slash); - cs->tilde_expanded = true; - char *dir = path_dirname(cs->parsed); - char *dirprefix = path_dirname(str); - do_collect_files(&cs->completions, dir, dirprefix, slash + 1, type); - free(dirprefix); - free(dir); - free(str); - } else { - const char *slash = strrchr(cs->parsed, '/'); - if (!slash) { - do_collect_files(&cs->completions, ".", "", cs->parsed, type); - } else { - char *dir = path_dirname(cs->parsed); - do_collect_files(&cs->completions, dir, dir, slash + 1, type); - free(dir); - } - } - - if (cs->completions.count == 1) { - // Add space if completed string is not a directory - const char *s = cs->completions.ptrs[0]; - size_t len = strlen(s); - if (len > 0) { - cs->add_space_after_single_match = s[len - 1] != '/'; - } - } -} - -void collect_normal_aliases(EditorState *e, PointerArray *a, const char *prefix) -{ - collect_hashmap_keys(&e->aliases, a, prefix); -} - -static void collect_bound_keys(const IntMap *bindings, PointerArray *a, const char *prefix) -{ - char keystr[KEYCODE_STR_MAX]; - for (IntMapIter it = intmap_iter(bindings); intmap_next(&it); ) { - size_t keylen = keycode_to_string(it.entry->key, keystr); - if (str_has_prefix(keystr, prefix)) { - ptr_array_append(a, xmemdup(keystr, keylen + 1)); - } - } -} - -void collect_bound_normal_keys(EditorState *e, PointerArray *a, const char *prefix) -{ - collect_bound_keys(&e->modes[INPUT_NORMAL].key_bindings, a, prefix); -} - -void collect_hl_colors(EditorState *e, PointerArray *a, const char *prefix) -{ - collect_builtin_colors(a, prefix); - collect_hashmap_keys(&e->colors.other, a, prefix); -} - -void collect_compilers(EditorState *e, PointerArray *a, const char *prefix) -{ - collect_hashmap_keys(&e->compilers, a, prefix); -} - -void collect_env(EditorState* UNUSED_ARG(e), PointerArray *a, const char *prefix) -{ - if (strchr(prefix, '=')) { - return; - } - - for (size_t i = 0; environ[i]; i++) { - const char *var = environ[i]; - if (str_has_prefix(var, prefix)) { - const char *delim = strchr(var, '='); - if (likely(delim && delim != var)) { - ptr_array_append(a, xstrcut(var, delim - var)); - } - } - } -} - -static void complete_alias(EditorState *e, const CommandArgs *a) -{ - CompletionState *cs = &e->cmdline.completion; - if (a->nr_args == 0) { - collect_normal_aliases(e, &cs->completions, cs->parsed); - } else if (a->nr_args == 1 && cs->parsed[0] == '\0') { - const char *cmd = find_alias(&e->aliases, a->args[0]); - if (cmd) { - ptr_array_append(&cs->completions, xstrdup(cmd)); - } - } -} - -static void complete_bind(EditorState *e, const CommandArgs *a) -{ - static const char flags[] = { - [INPUT_NORMAL] = 'n', - [INPUT_COMMAND] = 'c', - [INPUT_SEARCH] = 's', - }; - - static_assert(ARRAYLEN(flags) == ARRAYLEN(e->modes)); - InputMode mode = INPUT_NORMAL; - for (size_t i = 0, count = 0; i < ARRAYLEN(flags); i++) { - if (cmdargs_has_flag(a, flags[i])) { - if (++count >= 2) { - return; // Don't complete bindings for multiple modes - } - mode = i; - } - } - - const IntMap *key_bindings = &e->modes[mode].key_bindings; - CompletionState *cs = &e->cmdline.completion; - if (a->nr_args == 0) { - collect_bound_keys(key_bindings, &cs->completions, cs->parsed); - return; - } - - if (a->nr_args != 1 || cs->parsed[0] != '\0') { - return; - } - - KeyCode key; - if (!parse_key_string(&key, a->args[0])) { - return; - } - const CachedCommand *cmd = lookup_binding(key_bindings, key); - if (!cmd) { - return; - } - - ptr_array_append(&cs->completions, xstrdup(cmd->cmd_str)); -} - -static void complete_cd(EditorState *e, const CommandArgs* UNUSED_ARG(a)) -{ - CompletionState *cs = &e->cmdline.completion; - collect_files(e, cs, COLLECT_DIRS_ONLY); - if (str_has_prefix("-", cs->parsed)) { - if (likely(xgetenv("OLDPWD"))) { - ptr_array_append(&cs->completions, xstrdup("-")); - } - } -} - -static void complete_exec(EditorState *e, const CommandArgs *a) -{ - // TODO: add completion for [-ioe] option arguments - CompletionState *cs = &e->cmdline.completion; - collect_files(e, cs, a->nr_args == 0 ? COLLECT_EXECUTABLES : COLLECT_ALL); -} - -static void complete_compile(EditorState *e, const CommandArgs *a) -{ - CompletionState *cs = &e->cmdline.completion; - size_t n = a->nr_args; - if (n == 0) { - collect_compilers(e, &cs->completions, cs->parsed); - } else { - collect_files(e, cs, n == 1 ? COLLECT_EXECUTABLES : COLLECT_ALL); - } -} - -static void complete_cursor(EditorState *e, const CommandArgs *a) -{ - CompletionState *cs = &e->cmdline.completion; - size_t n = a->nr_args; - if (n == 0) { - collect_cursor_modes(&cs->completions, cs->parsed); - } else if (n == 1) { - collect_cursor_types(&cs->completions, cs->parsed); - } else if (n == 2) { - collect_cursor_colors(&cs->completions, cs->parsed); - // Add an example #rrggbb color, to make things more discoverable - static const char rgb_example[] = "#22AABB"; - if (str_has_prefix(rgb_example, cs->parsed)) { - ptr_array_append(&cs->completions, xstrdup(rgb_example)); - } - } -} - -static void complete_errorfmt(EditorState *e, const CommandArgs *a) -{ - CompletionState *cs = &e->cmdline.completion; - if (a->nr_args == 0) { - collect_compilers(e, &cs->completions, cs->parsed); - } else if (a->nr_args >= 2 && !cmdargs_has_flag(a, 'i')) { - collect_errorfmt_capture_names(&cs->completions, cs->parsed); - } -} - -static void complete_ft(EditorState *e, const CommandArgs *a) -{ - CompletionState *cs = &e->cmdline.completion; - if (a->nr_args == 0) { - collect_ft(&e->filetypes, &cs->completions, cs->parsed); - } -} - -static void complete_hi(EditorState *e, const CommandArgs *a) -{ - CompletionState *cs = &e->cmdline.completion; - if (a->nr_args == 0) { - collect_hl_colors(e, &cs->completions, cs->parsed); - } else { - collect_colors_and_attributes(&cs->completions, cs->parsed); - } -} - -static void complete_include(EditorState *e, const CommandArgs *a) -{ - CompletionState *cs = &e->cmdline.completion; - if (a->nr_args == 0) { - if (cmdargs_has_flag(a, 'b')) { - collect_builtin_includes(&cs->completions, cs->parsed); - } else { - collect_files(e, cs, COLLECT_ALL); - } - } -} - -static void complete_macro(EditorState *e, const CommandArgs *a) -{ - static const char verbs[][8] = { - "cancel", - "play", - "record", - "stop", - "toggle", - }; - - if (a->nr_args != 0) { - return; - } - - CompletionState *cs = &e->cmdline.completion; - COLLECT_STRINGS(verbs, &cs->completions, cs->parsed); -} - -static void complete_move_tab(EditorState *e, const CommandArgs *a) -{ - if (a->nr_args != 0) { - return; - } - - static const char words[][8] = {"left", "right"}; - CompletionState *cs = &e->cmdline.completion; - COLLECT_STRINGS(words, &cs->completions, cs->parsed); -} - -static void complete_open(EditorState *e, const CommandArgs *a) -{ - if (!cmdargs_has_flag(a, 't')) { - collect_files(e, &e->cmdline.completion, COLLECT_ALL); - } -} - -static void complete_option(EditorState *e, const CommandArgs *a) -{ - CompletionState *cs = &e->cmdline.completion; - if (a->nr_args == 0) { - if (!cmdargs_has_flag(a, 'r')) { - collect_ft(&e->filetypes, &cs->completions, cs->parsed); - } - } else if (a->nr_args & 1) { - collect_auto_options(&cs->completions, cs->parsed); - } else { - collect_option_values(e, &cs->completions, a->args[a->nr_args - 1], cs->parsed); - } -} - -static void complete_save(EditorState *e, const CommandArgs* UNUSED_ARG(a)) -{ - collect_files(e, &e->cmdline.completion, COLLECT_ALL); -} - -static void complete_quit(EditorState *e, const CommandArgs* UNUSED_ARG(a)) -{ - CompletionState *cs = &e->cmdline.completion; - if (str_has_prefix("0", cs->parsed)) { - ptr_array_append(&cs->completions, xstrdup("0")); - } - if (str_has_prefix("1", cs->parsed)) { - ptr_array_append(&cs->completions, xstrdup("1")); - } -} - -static void complete_redo(EditorState *e, const CommandArgs* UNUSED_ARG(a)) -{ - const Change *change = e->buffer->cur_change; - CompletionState *cs = &e->cmdline.completion; - for (unsigned long i = 1, n = change->nr_prev; i <= n; i++) { - ptr_array_append(&cs->completions, xstrdup(ulong_to_str(i))); - } -} - -static void complete_set(EditorState *e, const CommandArgs *a) -{ - CompletionState *cs = &e->cmdline.completion; - if ((a->nr_args + 1) & 1) { - bool local = cmdargs_has_flag(a, 'l'); - bool global = cmdargs_has_flag(a, 'g'); - collect_options(&cs->completions, cs->parsed, local, global); - } else { - collect_option_values(e, &cs->completions, a->args[a->nr_args - 1], cs->parsed); - } -} - -static void complete_setenv(EditorState *e, const CommandArgs *a) -{ - CompletionState *cs = &e->cmdline.completion; - if (a->nr_args == 0) { - collect_env(e, &cs->completions, cs->parsed); - } else if (a->nr_args == 1 && cs->parsed[0] == '\0') { - BUG_ON(!a->args[0]); - const char *value = getenv(a->args[0]); - if (value) { - ptr_array_append(&cs->completions, xstrdup(value)); - } - } -} - -static void complete_show(EditorState *e, const CommandArgs *a) -{ - CompletionState *cs = &e->cmdline.completion; - if (a->nr_args == 0) { - collect_show_subcommands(&cs->completions, cs->parsed); - } else if (a->nr_args == 1) { - BUG_ON(!a->args[0]); - collect_show_subcommand_args(e, &cs->completions, a->args[0], cs->parsed); - } -} - -static void complete_tag(EditorState *e, const CommandArgs *a) -{ - CompletionState *cs = &e->cmdline.completion; - if (a->nr_args == 0 && !cmdargs_has_flag(a, 'r')) { - BUG_ON(!cs->parsed); - StringView prefix = strview_from_cstring(cs->parsed); - collect_tags(&e->tagfile, &cs->completions, &prefix); - } -} - -static void complete_toggle(EditorState *e, const CommandArgs *a) -{ - CompletionState *cs = &e->cmdline.completion; - if (a->nr_args == 0) { - bool global = cmdargs_has_flag(a, 'g'); - collect_toggleable_options(&cs->completions, cs->parsed, global); - } -} - -static void complete_wsplit(EditorState *e, const CommandArgs *a) -{ - CompletionState *cs = &e->cmdline.completion; - if (!cmdargs_has_flag(a, 't') && !cmdargs_has_flag(a, 'n')) { - collect_files(e, cs, COLLECT_ALL); - } -} - -typedef struct { - char cmd_name[12]; - void (*complete)(EditorState *e, const CommandArgs *a); -} CompletionHandler; - -static const CompletionHandler completion_handlers[] = { - {"alias", complete_alias}, - {"bind", complete_bind}, - {"cd", complete_cd}, - {"compile", complete_compile}, - {"cursor", complete_cursor}, - {"errorfmt", complete_errorfmt}, - {"exec", complete_exec}, - {"ft", complete_ft}, - {"hi", complete_hi}, - {"include", complete_include}, - {"macro", complete_macro}, - {"move-tab", complete_move_tab}, - {"open", complete_open}, - {"option", complete_option}, - {"quit", complete_quit}, - {"redo", complete_redo}, - {"save", complete_save}, - {"set", complete_set}, - {"setenv", complete_setenv}, - {"show", complete_show}, - {"tag", complete_tag}, - {"toggle", complete_toggle}, - {"wsplit", complete_wsplit}, -}; - -UNITTEST { - CHECK_BSEARCH_ARRAY(completion_handlers, cmd_name, strcmp); - // Ensure handlers are kept in sync with renamed/removed commands - for (size_t i = 0; i < ARRAYLEN(completion_handlers); i++) { - const char *name = completion_handlers[i].cmd_name; - if (!find_normal_command(name)) { - BUG("completion handler for non-existent command: \"%s\"", name); - } - } -} - -static bool can_collect_flags ( - char **args, - size_t argc, - size_t nr_flag_args, - bool allow_flags_after_nonflags -) { - if (allow_flags_after_nonflags) { - for (size_t i = 0; i < argc; i++) { - if (streq(args[i], "--")) { - return false; - } - } - return true; - } - - for (size_t i = 0, nonflag = 0; i < argc; i++) { - if (args[i][0] != '-') { - if (++nonflag > nr_flag_args) { - return false; - } - continue; - } - if (streq(args[i], "--")) { - return false; - } - } - - return true; -} - -static bool collect_command_flags ( - PointerArray *array, - char **args, - size_t argc, - const Command *cmd, - const CommandArgs *a, - const char *prefix -) { - BUG_ON(prefix[0] != '-'); - const char *flags = cmd->flags; - bool flags_after_nonflags = (flags[0] != '-'); - - if (!can_collect_flags(args, argc, a->nr_flag_args, flags_after_nonflags)) { - return false; - } - - flags += flags_after_nonflags ? 0 : 1; - if (ascii_isalnum(prefix[1]) && prefix[2] == '\0') { - if (strchr(flags, prefix[1])) { - ptr_array_append(array, xmemdup(prefix, 3)); - } - return true; - } - - if (prefix[1] != '\0') { - return true; - } - - char buf[3] = "-"; - for (size_t i = 0; flags[i]; i++) { - if (!ascii_isalnum(flags[i]) || cmdargs_has_flag(a, flags[i])) { - continue; - } - buf[1] = flags[i]; - ptr_array_append(array, xmemdup(buf, 3)); - } - - return true; -} - -static void collect_completions(EditorState *e, char **args, size_t argc) -{ - CompletionState *cs = &e->cmdline.completion; - PointerArray *arr = &cs->completions; - const char *prefix = cs->parsed; - if (!argc) { - collect_normal_commands(arr, prefix); - collect_normal_aliases(e, arr, prefix); - return; - } - - for (size_t i = 0; i < argc; i++) { - if (!args[i]) { - // Embedded NULLs indicate there are multiple commands. - // Just return early here and avoid handling this case. - return; - } - } - - const Command *cmd = find_normal_command(args[0]); - if (!cmd) { - return; - } - - char **args_copy = copy_string_array(args + 1, argc - 1); - CommandArgs a = cmdargs_new(args_copy); - ArgParseError err = do_parse_args(cmd, &a); - bool dash = (prefix[0] == '-'); - if ( - (err != ARGERR_NONE && err != ARGERR_TOO_FEW_ARGUMENTS) - || (a.nr_args >= cmd->max_args && cmd->max_args != 0xFF && !dash) - ) { - goto out; - } - - if (dash && collect_command_flags(arr, args + 1, argc - 1, cmd, &a, prefix)) { - goto out; - } - - if (cmd->max_args == 0) { - goto out; - } - - const CompletionHandler *h = BSEARCH(args[0], completion_handlers, vstrcmp); - if (h) { - h->complete(e, &a); - } else if (streq(args[0], "repeat")) { - if (a.nr_args == 1) { - collect_normal_commands(arr, prefix); - } else if (a.nr_args >= 2) { - collect_completions(e, args + 2, argc - 2); - } - } - -out: - free_string_array(args_copy); -} - -static bool is_var(const char *str, size_t len) -{ - if (len == 0 || str[0] != '$') { - return false; - } - if (len == 1) { - return true; - } - if (!is_alpha_or_underscore(str[1])) { - return false; - } - for (size_t i = 2; i < len; i++) { - if (!is_alnum_or_underscore(str[i])) { - return false; - } - } - return true; -} - -UNITTEST { - BUG_ON(!is_var(STRN("$VAR"))); - BUG_ON(!is_var(STRN("$xy_190"))); - BUG_ON(!is_var(STRN("$__x_y_z"))); - BUG_ON(!is_var(STRN("$x"))); - BUG_ON(!is_var(STRN("$A"))); - BUG_ON(!is_var(STRN("$_0"))); - BUG_ON(!is_var(STRN("$"))); - BUG_ON(is_var(STRN(""))); - BUG_ON(is_var(STRN("A"))); - BUG_ON(is_var(STRN("$.a"))); - BUG_ON(is_var(STRN("$xyz!"))); - BUG_ON(is_var(STRN("$1"))); - BUG_ON(is_var(STRN("$09"))); - BUG_ON(is_var(STRN("$1a"))); -} - -static int strptrcmp(const void *v1, const void *v2) -{ - const char *const *s1 = v1; - const char *const *s2 = v2; - return strcmp(*s1, *s2); -} - -static void init_completion(EditorState *e, const CommandLine *cmdline) -{ - CompletionState *cs = &e->cmdline.completion; - const CommandRunner runner = cmdrunner_for_mode(e, INPUT_NORMAL, false); - BUG_ON(cs->orig); - BUG_ON(runner.userdata != e); - BUG_ON(!runner.lookup_alias); - - const size_t cmdline_pos = cmdline->pos; - char *const cmd = string_clone_cstring(&cmdline->buf); - PointerArray array = PTR_ARRAY_INIT; - ssize_t semicolon = -1; - ssize_t completion_pos = -1; - - for (size_t pos = 0; true; ) { - while (ascii_isspace(cmd[pos])) { - pos++; - } - - if (pos >= cmdline_pos) { - completion_pos = cmdline_pos; - break; - } - - if (!cmd[pos]) { - break; - } - - if (cmd[pos] == ';') { - semicolon = array.count; - ptr_array_append(&array, NULL); - pos++; - continue; - } - - CommandParseError err; - size_t end = find_end(cmd, pos, &err); - if (err != CMDERR_NONE || end >= cmdline_pos) { - completion_pos = pos; - break; - } - - if (semicolon + 1 == array.count) { - char *name = xstrslice(cmd, pos, end); - const char *value = runner.lookup_alias(name, runner.userdata); - if (value) { - size_t save = array.count; - if (parse_commands(&runner, &array, value) != CMDERR_NONE) { - for (size_t i = save, n = array.count; i < n; i++) { - free(array.ptrs[i]); - array.ptrs[i] = NULL; - } - array.count = save; - ptr_array_append(&array, parse_command_arg(&runner, name, end - pos, true)); - } else { - // Remove NULL - array.count--; - } - } else { - ptr_array_append(&array, parse_command_arg(&runner, name, end - pos, true)); - } - free(name); - } else { - ptr_array_append(&array, parse_command_arg(&runner, cmd + pos, end - pos, true)); - } - pos = end; - } - - const char *str = cmd + completion_pos; - size_t len = cmdline_pos - completion_pos; - if (is_var(str, len)) { - char *name = xstrslice(str, 1, len); - completion_pos++; - collect_env(e, &cs->completions, name); - collect_normal_vars(&cs->completions, name); - free(name); - } else { - cs->escaped = string_view(str, len); - cs->parsed = parse_command_arg(&runner, str, len, true); - cs->add_space_after_single_match = true; - size_t count = array.count; - char **args = count ? (char**)array.ptrs + 1 + semicolon : NULL; - size_t argc = count ? array.count - semicolon - 1 : 0; - collect_completions(e, args, argc); - } - - ptr_array_free(&array); - ptr_array_sort(&cs->completions, strptrcmp); - cs->orig = cmd; // (takes ownership) - cs->tail = strview_from_cstring(cmd + cmdline_pos); - cs->head_len = completion_pos; -} - -static void do_complete_command(CommandLine *cmdline) -{ - const CompletionState *cs = &cmdline->completion; - const PointerArray *arr = &cs->completions; - const StringView middle = strview_from_cstring(arr->ptrs[cs->idx]); - const StringView tail = cs->tail; - const size_t head_length = cs->head_len; - - String buf = string_new(head_length + tail.length + middle.length + 16); - string_append_buf(&buf, cs->orig, head_length); - string_append_escaped_arg_sv(&buf, middle, !cs->tilde_expanded); - - bool single_completion = (arr->count == 1); - if (single_completion && cs->add_space_after_single_match) { - string_append_byte(&buf, ' '); - } - - size_t pos = buf.len; - string_append_strview(&buf, &tail); - cmdline_set_text(cmdline, string_borrow_cstring(&buf)); - cmdline->pos = pos; - string_free(&buf); - - if (single_completion) { - reset_completion(cmdline); - } -} - -void complete_command_next(EditorState *e) -{ - CompletionState *cs = &e->cmdline.completion; - const bool init = !cs->orig; - if (init) { - init_completion(e, &e->cmdline); - } - size_t count = cs->completions.count; - if (!count) { - return; - } - if (!init) { - cs->idx = size_increment_wrapped(cs->idx, count); - } - do_complete_command(&e->cmdline); -} - -void complete_command_prev(EditorState *e) -{ - CompletionState *cs = &e->cmdline.completion; - const bool init = !cs->orig; - if (init) { - init_completion(e, &e->cmdline); - } - size_t count = cs->completions.count; - if (!count) { - return; - } - if (!init) { - cs->idx = size_decrement_wrapped(cs->idx, count); - } - do_complete_command(&e->cmdline); -} - -void reset_completion(CommandLine *cmdline) -{ - CompletionState *cs = &cmdline->completion; - free(cs->parsed); - free(cs->orig); - ptr_array_free(&cs->completions); - *cs = (CompletionState){.orig = NULL}; -} - -void collect_hashmap_keys(const HashMap *map, PointerArray *a, const char *prefix) -{ - for (HashMapIter it = hashmap_iter(map); hashmap_next(&it); ) { - const char *name = it.entry->key; - if (str_has_prefix(name, prefix)) { - ptr_array_append(a, xstrdup(name)); - } - } -} |
