diff options
Diffstat (limited to 'src/main/cmds.c')
| -rw-r--r-- | src/main/cmds.c | 366 |
1 files changed, 71 insertions, 295 deletions
diff --git a/src/main/cmds.c b/src/main/cmds.c index 5991f4a..bf465ed 100644 --- a/src/main/cmds.c +++ b/src/main/cmds.c @@ -1,11 +1,7 @@ -#define _DEFAULT_SOURCE -#include <dirent.h> #include <errno.h> -#include <libgen.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> -#include <sys/types.h> #include "dged/binding.h" #include "dged/buffer.h" @@ -18,16 +14,15 @@ #include "dged/settings.h" #include "bindings.h" +#include "completion.h" #include "search-replace.h" -static void abort_completion(); - int32_t _abort(struct command_ctx ctx, int argc, const char *argv[]) { abort_replace(); abort_completion(); + disable_completion(minibuffer_buffer()); minibuffer_abort_prompt(); buffer_view_clear_mark(window_buffer_view(ctx.active_window)); - reset_minibuffer_keys(minibuffer_buffer()); minibuffer_echo_timeout(4, "💣 aborted"); return 0; } @@ -43,291 +38,6 @@ int32_t exit_editor(struct command_ctx ctx, int argc, const char *argv[]) { return 0; } -struct completion { - const char *display; - const char *insert; - bool complete; -}; - -uint32_t g_ncompletions = 0; -struct completion g_completions[50] = {0}; - -static void abort_completion() { - reset_minibuffer_keys(minibuffer_buffer()); - windows_close_popup(); - - for (uint32_t compi = 0; compi < g_ncompletions; ++compi) { - free((void *)g_completions[compi].display); - free((void *)g_completions[compi].insert); - } - g_ncompletions = 0; -} - -int cmp_completions(const void *comp_a, const void *comp_b) { - struct completion *a = (struct completion *)comp_a; - struct completion *b = (struct completion *)comp_b; - return strcmp(a->display, b->display); -} - -static bool is_hidden(const char *filename) { - return filename[0] == '.' && filename[1] != '\0' && filename[1] != '.'; -} - -static void complete_path(const char *path, struct completion results[], - uint32_t nresults_max, uint32_t *nresults) { - uint32_t n = 0; - char *p1 = to_abspath(path); - size_t len = strlen(p1); - char *p2 = strdup(p1); - - size_t inlen = strlen(path); - - if (len == 0) { - goto done; - } - - if (nresults_max == 0) { - goto done; - } - - const char *dir = p1; - const char *file = ""; - - // check the input path here since - // to_abspath removes trailing slashes - if (path[inlen - 1] != '/') { - dir = dirname(p1); - file = basename(p2); - } - - DIR *d = opendir(dir); - if (d == NULL) { - goto done; - } - - errno = 0; - while (n < nresults_max) { - struct dirent *de = readdir(d); - if (de == NULL && errno != 0) { - // skip the erroring entry - errno = 0; - continue; - } else if (de == NULL && errno == 0) { - break; - } - - switch (de->d_type) { - case DT_DIR: - case DT_REG: - case DT_LNK: - if (!is_hidden(de->d_name) && - (strncmp(file, de->d_name, strlen(file)) == 0 || strlen(file) == 0)) { - const char *disp = strdup(de->d_name); - results[n] = (struct completion){ - .display = disp, - .insert = strdup(disp + strlen(file)), - .complete = de->d_type == DT_REG, - }; - ++n; - } - break; - } - } - - closedir(d); - -done: - free(p1); - free(p2); - - qsort(results, n, sizeof(struct completion), cmp_completions); - *nresults = n; -} - -void update_completion_buffer(struct buffer *buffer, uint32_t width, - uint32_t height, void *userdata) { - struct buffer_view *view = (struct buffer_view *)userdata; - struct text_chunk line = buffer_line(buffer, view->dot.line); - buffer_add_text_property( - view->buffer, (struct location){.line = view->dot.line, .col = 0}, - (struct location){.line = view->dot.line, .col = line.nchars}, - (struct text_property){.type = TextProperty_Colors, - .colors = (struct text_property_colors){ - .set_bg = false, - .bg = 0, - .set_fg = true, - .fg = 4, - }}); - - if (line.allocated) { - free(line.text); - } -} - -static int32_t goto_completion(struct command_ctx ctx, int argc, - const char *argv[]) { - void (*movement_fn)(struct buffer_view *) = - (void (*)(struct buffer_view *))ctx.userdata; - struct buffer *b = buffers_find(ctx.buffers, "*completions*"); - - // is it in the popup? - if (b != NULL && window_buffer(popup_window()) == b) { - struct buffer_view *v = window_buffer_view(popup_window()); - movement_fn(v); - - if (v->dot.line >= text_num_lines(b->text)) { - buffer_view_backward_line(v); - } - } - - return 0; -} - -static int32_t insert_completion(struct command_ctx ctx, int argc, - const char *argv[]) { - struct buffer *b = buffers_find(ctx.buffers, "*completions*"); - // is it in the popup? - if (b != NULL && window_buffer(popup_window()) == b) { - struct buffer_view *cv = window_buffer_view(popup_window()); - - if (cv->dot.line < g_ncompletions) { - char *ins = (char *)g_completions[cv->dot.line].insert; - bool complete = g_completions[cv->dot.line].complete; - size_t inslen = strlen(ins); - buffer_view_add(window_buffer_view(windows_get_active()), ins, inslen); - - if (minibuffer_focused() && complete) { - minibuffer_execute(); - } - - abort_completion(); - } - } - - return 0; -} - -COMMAND_FN("next-completion", next_completion, goto_completion, - buffer_view_forward_line); -COMMAND_FN("prev-completion", prev_completion, goto_completion, - buffer_view_backward_line); -COMMAND_FN("insert-completion", insert_completion, insert_completion, NULL); - -static void on_find_file_input(void *userdata) { - struct buffers *buffers = (struct buffers *)userdata; - struct text_chunk txt = minibuffer_content(); - - struct window *mb = minibuffer_window(); - struct location mb_dot = window_buffer_view(mb)->dot; - struct window_position mbpos = window_position(mb); - - struct buffer *b = buffers_find(buffers, "*completions*"); - if (b == NULL) { - b = buffers_add(buffers, buffer_create("*completions*")); - buffer_add_update_hook(b, update_completion_buffer, - (void *)window_buffer_view(popup_window())); - window_set_buffer_e(popup_window(), b, false, false); - } - - struct buffer_view *v = window_buffer_view(popup_window()); - - char path[1024]; - strncpy(path, txt.text, txt.nbytes); - path[(txt.nbytes >= 1024 ? 1023 : txt.nbytes)] = '\0'; - - for (uint32_t compi = 0; compi < g_ncompletions; ++compi) { - free((void *)g_completions[compi].display); - free((void *)g_completions[compi].insert); - } - - g_ncompletions = 0; - complete_path(path, g_completions, 50, &g_ncompletions); - - size_t max_width = 0; - struct location prev_dot = v->dot; - - buffer_clear(v->buffer); - buffer_view_goto(v, (struct location){.line = 0, .col = 0}); - if (g_ncompletions > 0) { - for (uint32_t compi = 0; compi < g_ncompletions; ++compi) { - const char *disp = g_completions[compi].display; - size_t width = strlen(disp); - if (width > max_width) { - max_width = width; - } - buffer_view_add(v, (uint8_t *)disp, width); - - // the extra newline feels weird in navigation - if (compi != g_ncompletions - 1) { - buffer_view_add(v, (uint8_t *)"\n", 1); - } - } - - buffer_view_goto( - v, (struct location){.line = prev_dot.line, .col = prev_dot.col}); - if (prev_dot.line >= text_num_lines(b->text)) { - buffer_view_backward_line(v); - } - - if (!popup_window_visible()) { - struct binding bindings[] = { - ANONYMOUS_BINDING(Ctrl, 'N', &next_completion_command), - ANONYMOUS_BINDING(Ctrl, 'P', &prev_completion_command), - ANONYMOUS_BINDING(ENTER, &insert_completion_command), - }; - buffer_bind_keys(minibuffer_buffer(), bindings, - sizeof(bindings) / sizeof(bindings[0])); - } - - uint32_t width = max_width > 2 ? max_width : 4, - height = g_ncompletions > 10 ? 10 : g_ncompletions; - windows_show_popup(mbpos.y + mb_dot.line - height, mbpos.x + mb_dot.col, - width, height); - } else { - abort_completion(); - } - - if (txt.allocated) { - free(txt.text); - } -} - -int32_t find_file(struct command_ctx ctx, int argc, const char *argv[]) { - const char *pth = NULL; - if (argc == 0) { - return minibuffer_prompt_interactive(ctx, on_find_file_input, ctx.buffers, - "find file: "); - } - - pth = argv[0]; - struct stat sb = {0}; - if (stat(pth, &sb) < 0 && errno != ENOENT) { - minibuffer_echo("stat on %s failed: %s", pth, strerror(errno)); - return 1; - } - - if (S_ISDIR(sb.st_mode) && errno != ENOENT) { - minibuffer_echo("TODO: implement dired!"); - return 1; - } - - const char *filename = to_abspath(pth); - struct buffer *b = buffers_find_by_filename(ctx.buffers, filename); - free((char *)filename); - - if (b == NULL) { - b = buffers_add(ctx.buffers, buffer_from_file((char *)pth)); - } else { - buffer_reload(b); - } - - window_set_buffer(ctx.active_window, b); - minibuffer_echo_timeout(4, "buffer \"%s\" loaded", - window_buffer(ctx.active_window)->name); - - return 0; -} - int32_t write_file(struct command_ctx ctx, int argc, const char *argv[]) { const char *pth = NULL; if (argc == 0) { @@ -380,8 +90,16 @@ int32_t do_switch_buffer(struct command_ctx ctx, int argc, const char *argv[]) { COMMAND_FN("do-switch-buffer", do_switch_buffer, do_switch_buffer, NULL); +static void switch_buffer_comp_inserted() { minibuffer_execute(); } + int32_t switch_buffer(struct command_ctx ctx, int argc, const char *argv[]) { if (argc == 0) { + struct completion_provider providers[] = {buffer_provider()}; + enable_completion(minibuffer_buffer(), + ((struct completion_trigger){ + .kind = CompletionTrigger_Input, .nchars = 0}), + providers, 1, switch_buffer_comp_inserted); + ctx.self = &do_switch_buffer_command; if (window_has_prev_buffer(ctx.active_window)) { return minibuffer_prompt(ctx, "buffer (default %s): ", @@ -391,6 +109,8 @@ int32_t switch_buffer(struct command_ctx ctx, int argc, const char *argv[]) { } } + disable_completion(minibuffer_buffer()); + return execute_command(&do_switch_buffer_command, ctx.commands, ctx.active_window, ctx.buffers, argc, argv); } @@ -433,7 +153,7 @@ int32_t buflist_visit_cmd(struct command_ctx ctx, int argc, if (end != NULL) { uint32_t len = end - (char *)text.text; char *bufname = (char *)malloc(len + 1); - strncpy(bufname, text.text, len); + strncpy(bufname, (const char *)text.text, len); bufname[len] = '\0'; struct buffer *target = buffers_find(ctx.buffers, bufname); @@ -454,8 +174,8 @@ int32_t buflist_close_cmd(struct command_ctx ctx, int argc, void buflist_refresh(struct buffers *buffers, struct buffer_view *target) { buffer_set_readonly(target->buffer, false); buffer_clear(target->buffer); - buffers_for_each(buffers, buffer_to_list_line, target); buffer_view_goto_beginning(target); + buffers_for_each(buffers, buffer_to_list_line, target); buffer_set_readonly(target->buffer, true); } @@ -496,12 +216,58 @@ int32_t buffer_list(struct command_ctx ctx, int argc, const char *argv[]) { ANONYMOUS_BINDING(None, 'q', &buflist_close), ANONYMOUS_BINDING(None, 'g', &buflist_refresh), }; - buffer_bind_keys(b, bindings, sizeof(bindings) / sizeof(bindings[0])); + struct keymap km = keymap_create("buflist", 8); + keymap_bind_keys(&km, bindings, sizeof(bindings) / sizeof(bindings[0])); + buffer_add_keymap(b, km); windows_set_active(w); return 0; } +static void find_file_comp_inserted() { minibuffer_execute(); } + +int32_t find_file(struct command_ctx ctx, int argc, const char *argv[]) { + const char *pth = NULL; + if (argc == 0) { + struct completion_provider providers[] = {path_provider()}; + enable_completion(minibuffer_buffer(), + ((struct completion_trigger){ + .kind = CompletionTrigger_Input, .nchars = 0}), + providers, 1, find_file_comp_inserted); + return minibuffer_prompt(ctx, "find file: "); + } + + disable_completion(minibuffer_buffer()); + + pth = argv[0]; + struct stat sb = {0}; + if (stat(pth, &sb) < 0 && errno != ENOENT) { + minibuffer_echo("stat on %s failed: %s", pth, strerror(errno)); + return 1; + } + + if (S_ISDIR(sb.st_mode) && errno != ENOENT) { + minibuffer_echo("TODO: implement dired!"); + return 1; + } + + const char *filename = to_abspath(pth); + struct buffer *b = buffers_find_by_filename(ctx.buffers, filename); + free((char *)filename); + + if (b == NULL) { + b = buffers_add(ctx.buffers, buffer_from_file((char *)pth)); + } else { + buffer_reload(b); + } + + window_set_buffer(ctx.active_window, b); + minibuffer_echo_timeout(4, "buffer \"%s\" loaded", + window_buffer(ctx.active_window)->name); + + return 0; +} + void register_global_commands(struct commands *commands, void (*terminate_cb)()) { struct command global_commands[] = { @@ -591,6 +357,15 @@ static int32_t goto_line(struct command_ctx ctx, int argc, const char *argv[]) { line = line - 1; } buffer_view_goto(v, (struct location){.line = line, .col = 0}); + + return 0; +} + +static int32_t insert_tab(struct command_ctx ctx, int argc, + const char *argv[]) { + struct buffer_view *v = window_buffer_view(ctx.active_window); + buffer_view_add(v, (uint8_t *)"\t", 1); + return 0; } void register_buffer_commands(struct commands *commands) { @@ -610,6 +385,7 @@ void register_buffer_commands(struct commands *commands) { {.name = "beginning-of-line", .fn = goto_beginning_of_line_cmd}, {.name = "newline", .fn = newline_cmd}, {.name = "indent", .fn = indent_cmd}, + {.name = "insert-tab", .fn = insert_tab}, {.name = "buffer-write-to-file", .fn = to_file_cmd}, {.name = "set-mark", .fn = set_mark_cmd}, {.name = "clear-mark", .fn = clear_mark_cmd}, |
