summaryrefslogtreecommitdiff
path: root/src/main/cmds.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/cmds.c')
-rw-r--r--src/main/cmds.c366
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},