summaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'src/main')
-rw-r--r--src/main/bindings.c129
-rw-r--r--src/main/bindings.h14
-rw-r--r--src/main/cmds.c366
-rw-r--r--src/main/completion.c541
-rw-r--r--src/main/completion.h83
-rw-r--r--src/main/main.c40
-rw-r--r--src/main/search-replace.c23
7 files changed, 804 insertions, 392 deletions
diff --git a/src/main/bindings.c b/src/main/bindings.c
index 0e01306..32dcbdb 100644
--- a/src/main/bindings.c
+++ b/src/main/bindings.c
@@ -3,16 +3,19 @@
#include "dged/minibuffer.h"
#include "dged/vec.h"
+#include "bindings.h"
+
static struct keymap g_global_keymap, g_ctrlx_map, g_windows_keymap,
g_buffer_default_keymap;
struct buffer_keymap {
+ buffer_keymap_id id;
struct buffer *buffer;
- bool active;
struct keymap keymap;
};
static VEC(struct buffer_keymap) g_buffer_keymaps;
+static buffer_keymap_id g_current_keymap_id;
void set_default_buffer_bindings(struct keymap *keymap) {
struct binding buffer_bindings[] = {
@@ -46,6 +49,7 @@ void set_default_buffer_bindings(struct keymap *keymap) {
BINDING(ENTER, "newline"),
BINDING(TAB, "indent"),
+ BINDING(Spec, 'Z', "insert-tab"),
BINDING(Ctrl, 'K', "kill-line"),
BINDING(DELETE, "delete-char"),
@@ -68,7 +72,18 @@ void set_default_buffer_bindings(struct keymap *keymap) {
sizeof(buffer_bindings) / sizeof(buffer_bindings[0]));
}
-struct keymap *register_bindings() {
+int32_t execute(struct command_ctx ctx, int argc, const char *argv[]) {
+ // TODO: this should be more lib-like
+ return minibuffer_execute();
+}
+
+static struct command execute_minibuffer_command = {
+ .fn = execute,
+ .name = "minibuffer-execute",
+ .userdata = NULL,
+};
+
+void init_bindings() {
g_global_keymap = keymap_create("global", 32);
g_ctrlx_map = keymap_create("c-x", 32);
g_windows_keymap = keymap_create("c-x w", 32);
@@ -122,93 +137,79 @@ struct keymap *register_bindings() {
sizeof(ctrlx_bindings) / sizeof(ctrlx_bindings[0]));
VEC_INIT(&g_buffer_keymaps, 32);
+ g_current_keymap_id = 0;
- return &g_global_keymap;
+ /* Minibuffer binds.
+ * This map is actually never removed so forget about the id.
+ */
+ struct binding minibuffer_binds[] = {
+ ANONYMOUS_BINDING(ENTER, &execute_minibuffer_command),
+ };
+ struct keymap minibuffer_map = keymap_create("minibuffer", 8);
+ keymap_bind_keys(&minibuffer_map, minibuffer_binds,
+ sizeof(minibuffer_binds) / sizeof(minibuffer_binds[0]));
+ buffer_add_keymap(minibuffer_buffer(), minibuffer_map);
}
-struct keymap *buffer_default_bindings() {
- return &g_buffer_default_keymap;
-}
+buffer_keymap_id buffer_add_keymap(struct buffer *buffer,
+ struct keymap keymap) {
+ buffer_keymap_id id = ++g_current_keymap_id;
+ VEC_PUSH(&g_buffer_keymaps, ((struct buffer_keymap){
+ .id = id,
+ .buffer = buffer,
+ .keymap = keymap,
+ }));
-int32_t execute(struct command_ctx ctx, int argc, const char *argv[]) {
- // TODO: this should be more lib-like
- return minibuffer_execute();
+ return id;
}
-static struct command execute_minibuffer_command = {
- .fn = execute,
- .name = "minibuffer-execute",
- .userdata = NULL,
-};
-
-void buffer_bind_keys(struct buffer *buffer, struct binding *bindings,
- uint32_t nbindings) {
- struct buffer_keymap *target = NULL;
- VEC_FOR_EACH(&g_buffer_keymaps, struct buffer_keymap * km) {
- if (buffer == km->buffer) {
- target = km;
+void buffer_remove_keymap(buffer_keymap_id id) {
+ VEC_FOR_EACH_INDEXED(&g_buffer_keymaps, struct buffer_keymap * km, i) {
+ if (km->id == id) {
+ VEC_SWAP(&g_buffer_keymaps, i, VEC_SIZE(&g_buffer_keymaps) - 1);
+ VEC_POP(&g_buffer_keymaps, struct buffer_keymap removed);
+ keymap_destroy(&removed.keymap);
}
}
+}
- if (target == NULL) {
- struct buffer_keymap new = (struct buffer_keymap){
- .buffer = buffer,
- .active = false,
- };
- VEC_PUSH(&g_buffer_keymaps, new);
- target = VEC_BACK(&g_buffer_keymaps);
- }
+uint32_t buffer_keymaps(struct buffer *buffer, struct keymap *keymaps[],
+ uint32_t max_nkeymaps) {
+ uint32_t nkeymaps = 0;
- if (!target->active) {
- target->keymap = keymap_create("buffer-overlay-keys", 32);
- target->active = true;
- set_default_buffer_bindings(&target->keymap);
+ // global keybinds
+ if (nkeymaps < max_nkeymaps) {
+ keymaps[nkeymaps] = &g_global_keymap;
+ ++nkeymaps;
}
- keymap_bind_keys(&target->keymap, bindings, nbindings);
-}
-
-// TODO: do something better
-void reset_buffer_keys(struct buffer *buffer) {
- VEC_FOR_EACH(&g_buffer_keymaps, struct buffer_keymap * km) {
- if (buffer == km->buffer) {
- keymap_destroy(&km->keymap);
- km->active = false;
- }
+ // buffer keybinds
+ if (nkeymaps < max_nkeymaps) {
+ keymaps[nkeymaps] = &g_buffer_default_keymap;
+ ++nkeymaps;
}
-}
-struct keymap *buffer_keymap(struct buffer *buffer) {
- VEC_FOR_EACH(&g_buffer_keymaps, struct buffer_keymap * km) {
- if (buffer == km->buffer && km->active) {
- return &km->keymap;
+ // keybinds specific to this buffer
+ if (nkeymaps < max_nkeymaps) {
+ VEC_FOR_EACH(&g_buffer_keymaps, struct buffer_keymap * km) {
+ if (buffer == km->buffer && nkeymaps < max_nkeymaps) {
+ keymaps[nkeymaps] = &km->keymap;
+ ++nkeymaps;
+ }
}
}
- return &g_buffer_default_keymap;
+ return nkeymaps;
}
-void reset_minibuffer_keys(struct buffer *minibuffer) {
- reset_buffer_keys(minibuffer);
- struct binding bindings[] = {
- ANONYMOUS_BINDING(ENTER, &execute_minibuffer_command),
- };
-
- buffer_bind_keys(minibuffer, bindings,
- sizeof(bindings) / sizeof(bindings[0]));
-}
-
-void destroy_keymaps() {
+void destroy_bindings() {
keymap_destroy(&g_windows_keymap);
keymap_destroy(&g_global_keymap);
keymap_destroy(&g_ctrlx_map);
keymap_destroy(&g_buffer_default_keymap);
VEC_FOR_EACH(&g_buffer_keymaps, struct buffer_keymap * km) {
- if (km->active) {
- keymap_destroy(&km->keymap);
- km->active = false;
- }
+ keymap_destroy(&km->keymap);
}
VEC_DESTROY(&g_buffer_keymaps);
diff --git a/src/main/bindings.h b/src/main/bindings.h
index d0ba27c..4fd760a 100644
--- a/src/main/bindings.h
+++ b/src/main/bindings.h
@@ -4,12 +4,12 @@ struct keymap;
struct buffer;
struct binding;
-struct keymap *register_bindings();
+void init_bindings();
-void buffer_bind_keys(struct buffer *buffer, struct binding *bindings,
- uint32_t nbindings);
-void reset_buffer_keys(struct buffer *buffer);
-void reset_minibuffer_keys(struct buffer *minibuffer);
-struct keymap *buffer_keymap(struct buffer *buffer);
+typedef uint64_t buffer_keymap_id;
+buffer_keymap_id buffer_add_keymap(struct buffer *buffer, struct keymap keymap);
+void buffer_remove_keymap(buffer_keymap_id id);
+uint32_t buffer_keymaps(struct buffer *buffer, struct keymap *keymaps[],
+ uint32_t max_nkeymaps);
-void destroy_keymaps();
+void destroy_bindings();
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},
diff --git a/src/main/completion.c b/src/main/completion.c
new file mode 100644
index 0000000..7382805
--- /dev/null
+++ b/src/main/completion.c
@@ -0,0 +1,541 @@
+#define _DEFAULT_SOURCE
+#include "completion.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <libgen.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "dged/binding.h"
+#include "dged/buffer.h"
+#include "dged/buffer_view.h"
+#include "dged/buffers.h"
+#include "dged/minibuffer.h"
+#include "dged/path.h"
+#include "dged/window.h"
+
+#include "bindings.h"
+
+struct active_completion_ctx {
+ struct completion_trigger trigger;
+ uint32_t trigger_current_nchars;
+ struct completion_provider *providers;
+ uint32_t nproviders;
+ insert_cb on_completion_inserted;
+};
+
+struct completion_state {
+ struct completion completions[50];
+ uint32_t ncompletions;
+ uint32_t current_completion;
+ bool active;
+ buffer_keymap_id keymap_id;
+ bool keymap_active;
+ struct active_completion_ctx *ctx;
+} g_state = {0};
+
+static struct buffer *g_target_buffer = NULL;
+
+static void hide_completion();
+
+static uint32_t complete_path(struct completion_context ctx, void *userdata);
+static struct completion_provider g_path_provider = {
+ .name = "path",
+ .complete = complete_path,
+ .userdata = NULL,
+};
+
+static uint32_t complete_buffers(struct completion_context ctx, void *userdata);
+static struct completion_provider g_buffer_provider = {
+ .name = "buffers",
+ .complete = complete_buffers,
+ .userdata = NULL,
+};
+
+struct completion_provider path_provider() {
+ return g_path_provider;
+}
+
+struct completion_provider buffer_provider() {
+ return g_buffer_provider;
+}
+
+struct active_completion {
+ struct buffer *buffer;
+ uint32_t insert_hook_id;
+ uint32_t remove_hook_id;
+};
+
+VEC(struct active_completion) g_active_completions;
+
+static int32_t goto_next_completion(struct command_ctx ctx, int argc,
+ const char *argv[]) {
+ if (g_state.current_completion < g_state.ncompletions - 1) {
+ ++g_state.current_completion;
+ }
+
+ if (completion_active()) {
+ buffer_view_goto(
+ window_buffer_view(popup_window()),
+ ((struct location){.line = g_state.current_completion, .col = 0}));
+ }
+
+ return 0;
+}
+
+static int32_t goto_prev_completion(struct command_ctx ctx, int argc,
+ const char *argv[]) {
+ if (g_state.current_completion > 0) {
+ --g_state.current_completion;
+ }
+
+ if (completion_active()) {
+ buffer_view_goto(
+ window_buffer_view(popup_window()),
+ ((struct location){.line = g_state.current_completion, .col = 0}));
+ }
+
+ return 0;
+}
+
+static int32_t insert_completion(struct command_ctx ctx, int argc,
+ const char *argv[]) {
+ // is it in the popup?
+ struct completion *comp = &g_state.completions[g_state.current_completion];
+ bool done = comp->complete;
+ const char *ins = comp->insert;
+ size_t inslen = strlen(ins);
+ buffer_view_add(window_buffer_view(windows_get_active()), (uint8_t *)ins,
+ inslen);
+
+ if (done) {
+ g_state.ctx->on_completion_inserted();
+ abort_completion();
+ }
+
+ return 0;
+}
+
+static void clear_completions() {
+ for (uint32_t ci = 0; ci < g_state.ncompletions; ++ci) {
+ free((void *)g_state.completions[ci].display);
+ free((void *)g_state.completions[ci].insert);
+ g_state.completions[ci].display = NULL;
+ g_state.completions[ci].insert = NULL;
+ g_state.completions[ci].complete = false;
+ }
+ g_state.ncompletions = 0;
+}
+
+COMMAND_FN("next-completion", next_completion, goto_next_completion, NULL);
+COMMAND_FN("prev-completion", prev_completion, goto_prev_completion, NULL);
+COMMAND_FN("insert-completion", insert_completion, insert_completion, NULL);
+
+static void update_completions(struct buffer *buffer,
+ struct active_completion_ctx *ctx,
+ struct location location) {
+ clear_completions();
+ for (uint32_t pi = 0; pi < ctx->nproviders; ++pi) {
+ struct completion_provider *provider = &ctx->providers[pi];
+
+ struct completion_context comp_ctx = (struct completion_context){
+ .buffer = buffer,
+ .location = location,
+ .max_ncompletions = 50 - g_state.ncompletions,
+ .completions = g_state.completions,
+ };
+
+ g_state.ncompletions += provider->complete(comp_ctx, provider->userdata);
+ }
+
+ window_set_buffer_e(popup_window(), g_target_buffer, false, false);
+ struct buffer_view *v = window_buffer_view(popup_window());
+
+ size_t max_width = 0;
+ uint32_t prev_selection = g_state.current_completion;
+
+ buffer_clear(v->buffer);
+ buffer_view_goto(v, (struct location){.line = 0, .col = 0});
+ if (g_state.ncompletions > 0) {
+ for (uint32_t compi = 0; compi < g_state.ncompletions; ++compi) {
+ const char *disp = g_state.completions[compi].display;
+ size_t width = strlen(disp);
+ if (width > max_width) {
+ max_width = width;
+ }
+ buffer_view_add(v, (uint8_t *)disp, width);
+ buffer_view_add(v, (uint8_t *)"\n", 1);
+ }
+
+ // select the closest one to previous selection
+ g_state.current_completion = prev_selection < g_state.ncompletions
+ ? prev_selection
+ : g_state.ncompletions - 1;
+
+ buffer_view_goto(
+ v, (struct location){.line = g_state.current_completion, .col = 0});
+
+ struct window *target_window = window_find_by_buffer(buffer);
+ struct window_position winpos = window_position(target_window);
+ struct buffer_view *view = window_buffer_view(target_window);
+ uint32_t height = g_state.ncompletions > 10 ? 10 : g_state.ncompletions;
+ windows_show_popup(winpos.y + location.line - height - 1,
+ winpos.x + view->fringe_width + location.col + 1,
+ max_width + 2, height);
+
+ if (!g_state.keymap_active) {
+ struct keymap km = keymap_create("completion", 8);
+ struct binding comp_bindings[] = {
+ ANONYMOUS_BINDING(Ctrl, 'N', &next_completion_command),
+ ANONYMOUS_BINDING(Ctrl, 'P', &prev_completion_command),
+ ANONYMOUS_BINDING(ENTER, &insert_completion_command),
+ };
+ keymap_bind_keys(&km, comp_bindings,
+ sizeof(comp_bindings) / sizeof(comp_bindings[0]));
+ g_state.keymap_id = buffer_add_keymap(buffer, km);
+ g_state.keymap_active = true;
+ }
+ } else {
+ hide_completion();
+ }
+}
+
+static void on_buffer_delete(struct buffer *buffer, struct region deleted,
+ void *userdata) {
+ struct active_completion_ctx *ctx = (struct active_completion_ctx *)userdata;
+
+ if (g_state.active) {
+ update_completions(buffer, ctx, deleted.begin);
+ }
+}
+
+static void on_buffer_insert(struct buffer *buffer, struct region inserted,
+ void *userdata) {
+ struct active_completion_ctx *ctx = (struct active_completion_ctx *)userdata;
+
+ if (!g_state.active) {
+ uint32_t nchars = 0;
+ switch (ctx->trigger.kind) {
+ case CompletionTrigger_Input:
+ for (uint32_t line = inserted.begin.line; line <= inserted.end.line;
+ ++line) {
+ nchars += buffer_num_chars(buffer, line);
+ }
+ nchars -=
+ inserted.begin.col +
+ (buffer_num_chars(buffer, inserted.end.line) - inserted.end.col);
+
+ ctx->trigger_current_nchars += nchars;
+
+ if (ctx->trigger_current_nchars < ctx->trigger.nchars) {
+ return;
+ }
+
+ ctx->trigger_current_nchars = 0;
+ break;
+
+ case CompletionTrigger_Char:
+ // TODO
+ break;
+ }
+
+ // activate completion
+ g_state.active = true;
+ g_state.ctx = ctx;
+ }
+
+ update_completions(buffer, ctx, inserted.end);
+}
+
+static void update_completion_buffer(struct buffer *buffer, void *userdata) {
+ buffer_add_text_property(
+ g_target_buffer,
+ (struct location){.line = g_state.current_completion, .col = 0},
+ (struct location){
+ .line = g_state.current_completion,
+ .col = buffer_num_chars(g_target_buffer, g_state.current_completion)},
+ (struct text_property){.type = TextProperty_Colors,
+ .colors = (struct text_property_colors){
+ .set_bg = false,
+ .bg = 0,
+ .set_fg = true,
+ .fg = 4,
+ }});
+}
+
+void init_completion(struct buffers *buffers) {
+ if (g_target_buffer == NULL) {
+ g_target_buffer = buffers_add(buffers, buffer_create("*completions*"));
+ buffer_add_update_hook(g_target_buffer, update_completion_buffer, NULL);
+ }
+
+ g_buffer_provider.userdata = buffers;
+ VEC_INIT(&g_active_completions, 32);
+}
+
+void enable_completion(struct buffer *source, struct completion_trigger trigger,
+ struct completion_provider *providers,
+ uint32_t nproviders, insert_cb on_completion_inserted) {
+ // check if we are already active
+ VEC_FOR_EACH(&g_active_completions, struct active_completion * c) {
+ if (c->buffer == source) {
+ disable_completion(source);
+ }
+ }
+
+ struct active_completion_ctx *ctx =
+ calloc(1, sizeof(struct active_completion_ctx));
+ ctx->trigger = trigger;
+ ctx->on_completion_inserted = on_completion_inserted;
+ ctx->nproviders = nproviders;
+ ctx->providers = calloc(nproviders, sizeof(struct completion_provider));
+ memcpy(ctx->providers, providers,
+ sizeof(struct completion_provider) * nproviders);
+
+ uint32_t insert_hook_id =
+ buffer_add_insert_hook(source, on_buffer_insert, ctx);
+ uint32_t remove_hook_id =
+ buffer_add_delete_hook(source, on_buffer_delete, ctx);
+
+ VEC_PUSH(&g_active_completions, ((struct active_completion){
+ .buffer = source,
+ .insert_hook_id = insert_hook_id,
+ .remove_hook_id = remove_hook_id,
+ }));
+}
+
+static void hide_completion() {
+ windows_close_popup();
+ if (g_state.active) {
+ buffer_remove_keymap(g_state.keymap_id);
+ g_state.keymap_active = false;
+ }
+}
+
+void abort_completion() {
+ hide_completion();
+ g_state.active = false;
+ clear_completions();
+}
+
+bool completion_active() {
+ return popup_window_visible() &&
+ window_buffer(popup_window()) == g_target_buffer && g_state.active;
+}
+
+static void cleanup_active_comp_ctx(void *userdata) {
+ struct active_completion_ctx *ctx = (struct active_completion_ctx *)userdata;
+ free(ctx->providers);
+ free(ctx);
+}
+
+static void do_nothing(void *userdata) {}
+
+static void cleanup_active_completion(struct active_completion *comp) {
+ buffer_remove_delete_hook(comp->buffer, comp->remove_hook_id, do_nothing);
+ buffer_remove_insert_hook(comp->buffer, comp->insert_hook_id,
+ cleanup_active_comp_ctx);
+}
+
+void disable_completion(struct buffer *buffer) {
+ VEC_FOR_EACH_INDEXED(&g_active_completions, struct active_completion * comp,
+ i) {
+ if (buffer == comp->buffer) {
+ VEC_SWAP(&g_active_completions, i, VEC_SIZE(&g_active_completions) - 1);
+ VEC_POP(&g_active_completions, struct active_completion removed);
+ cleanup_active_completion(&removed);
+ }
+ }
+}
+
+void destroy_completion() {
+ // clean up any active completions we might have
+ VEC_FOR_EACH(&g_active_completions, struct active_completion * comp) {
+ cleanup_active_completion(comp);
+ }
+ VEC_DESTROY(&g_active_completions);
+}
+
+static bool is_hidden(const char *filename) {
+ return filename[0] == '.' && filename[1] != '\0' && filename[1] != '.';
+}
+
+static 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 uint32_t complete_path(struct completion_context ctx, void *userdata) {
+
+ // obtain path from the buffer
+ struct text_chunk txt = {0};
+ uint32_t start_idx = 0;
+ if (ctx.buffer == minibuffer_buffer()) {
+ txt = minibuffer_content();
+ } else {
+ txt = buffer_line(ctx.buffer, ctx.location.line);
+ uint32_t end_idx = text_col_to_byteindex(
+ ctx.buffer->text, ctx.location.line, ctx.location.col);
+
+ for (uint32_t bytei = end_idx; bytei > 0; --bytei) {
+ if (txt.text[bytei] == ' ') {
+ start_idx = bytei + 1;
+ break;
+ }
+ }
+
+ if (start_idx >= end_idx) {
+ return 0;
+ }
+
+ txt.nbytes = end_idx - start_idx;
+ }
+
+ char *path = calloc(txt.nbytes + 1, sizeof(uint8_t));
+ memcpy(path, txt.text + start_idx, txt.nbytes);
+
+ if (txt.allocated) {
+ free(txt.text);
+ }
+
+ 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 (ctx.max_ncompletions == 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 < ctx.max_ncompletions) {
+ 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);
+ ctx.completions[n] = (struct completion){
+ .display = disp,
+ .insert = strdup(disp + strlen(file)),
+ .complete = de->d_type == DT_REG,
+ };
+ ++n;
+ }
+ break;
+ }
+ }
+
+ closedir(d);
+
+done:
+ free(path);
+ free(p1);
+ free(p2);
+
+ qsort(ctx.completions, n, sizeof(struct completion), cmp_completions);
+ return n;
+}
+
+struct buffer_match_ctx {
+ const char *needle;
+ struct completion *completions;
+ uint32_t max_ncompletions;
+ uint32_t ncompletions;
+};
+
+static void buffer_matches(struct buffer *buffer, void *userdata) {
+ struct buffer_match_ctx *ctx = (struct buffer_match_ctx *)userdata;
+
+ if (strncmp(ctx->needle, buffer->name, strlen(ctx->needle)) == 0 &&
+ ctx->ncompletions < ctx->max_ncompletions) {
+ ctx->completions[ctx->ncompletions] = (struct completion){
+ .display = strdup(buffer->name),
+ .insert = strdup(buffer->name + strlen(ctx->needle)),
+ .complete = true,
+ };
+ ++ctx->ncompletions;
+ }
+}
+
+static uint32_t complete_buffers(struct completion_context ctx,
+ void *userdata) {
+ struct buffers *buffers = (struct buffers *)userdata;
+ if (buffers == NULL) {
+ return 0;
+ }
+
+ struct text_chunk txt = {0};
+ uint32_t start_idx = 0;
+ if (ctx.buffer == minibuffer_buffer()) {
+ txt = minibuffer_content();
+ } else {
+ txt = buffer_line(ctx.buffer, ctx.location.line);
+ uint32_t end_idx = text_col_to_byteindex(
+ ctx.buffer->text, ctx.location.line, ctx.location.col);
+ for (uint32_t bytei = end_idx; bytei > 0; --bytei) {
+ if (txt.text[bytei] == ' ') {
+ start_idx = bytei + 1;
+ break;
+ }
+ }
+
+ if (start_idx >= end_idx) {
+ return 0;
+ }
+
+ txt.nbytes = end_idx - start_idx;
+ }
+
+ char *needle = calloc(txt.nbytes + 1, sizeof(uint8_t));
+ memcpy(needle, txt.text + start_idx, txt.nbytes);
+
+ if (txt.allocated) {
+ free(txt.text);
+ }
+
+ struct buffer_match_ctx match_ctx = (struct buffer_match_ctx){
+ .needle = needle,
+ .max_ncompletions = ctx.max_ncompletions,
+ .completions = ctx.completions,
+ .ncompletions = 0,
+ };
+ buffers_for_each(buffers, buffer_matches, &match_ctx);
+
+ free(needle);
+ return match_ctx.ncompletions;
+}
diff --git a/src/main/completion.h b/src/main/completion.h
new file mode 100644
index 0000000..751d152
--- /dev/null
+++ b/src/main/completion.h
@@ -0,0 +1,83 @@
+#ifndef _COMPLETION_H
+#define _COMPLETION_H
+
+#include "dged/location.h"
+
+struct buffer;
+struct buffers;
+
+struct completion {
+ const char *display;
+ const char *insert;
+ bool complete;
+};
+
+struct completion_context {
+ struct buffer *buffer;
+ const struct location location;
+ const uint32_t max_ncompletions;
+ struct completion *completions;
+};
+
+typedef uint32_t (*completion_fn)(struct completion_context ctx,
+ void *userdata);
+
+struct completion_provider {
+ char name[16];
+ completion_fn complete;
+ void *userdata;
+};
+
+enum completion_trigger_kind {
+ CompletionTrigger_Input = 0,
+ CompletionTrigger_Char = 1,
+};
+
+struct completion_trigger {
+ enum completion_trigger_kind kind;
+ union {
+ uint32_t c;
+ uint32_t nchars;
+ };
+};
+
+void init_completion(struct buffers *buffers);
+void destroy_completion();
+
+typedef void (*insert_cb)();
+
+/**
+ * Enable completions in the buffer @ref source.
+ *
+ * @param source [in] The buffer to provide completions for.
+ * @param trigger [in] The completion trigger to use for this completion.
+ * @param providers [in] The completion providers to use.
+ * @param nproviders [in] The number of providers in @ref providers.
+ */
+void enable_completion(struct buffer *source, struct completion_trigger trigger,
+ struct completion_provider *providers,
+ uint32_t nproviders, insert_cb on_completion_inserted);
+
+struct completion_provider path_provider();
+struct completion_provider buffer_provider();
+
+/**
+ * Abort any active completion.
+ */
+void abort_completion();
+
+/**
+ * Is a completion currently showing?
+ *
+ * @returns True if the completion window is showing completions.
+ */
+bool completion_active();
+
+/**
+ * Disable completion for @ref buffer.
+ *
+ * @param buffer [in] Buffer to disable completions for.
+ */
+void disable_completion(struct buffer *buffer);
+
+#endif
diff --git a/src/main/main.c b/src/main/main.c
index fd69cff..1bb3003 100644
--- a/src/main/main.c
+++ b/src/main/main.c
@@ -22,6 +22,7 @@
#include "bindings.h"
#include "cmds.h"
+#include "completion.h"
static struct frame_allocator frame_allocator;
@@ -103,7 +104,7 @@ void update_file_watches(struct reactor *reactor) {
// find the buffer we need to reload
VEC_FOR_EACH(&g_watched_files, struct watched_file * w) {
if (w->watch_id == ev.id) {
- if (ev.mask & LastEvent != 0) {
+ if ((ev.mask & LastEvent) != 0) {
w->watch_id = -1;
continue;
}
@@ -206,16 +207,8 @@ int main(int argc, char *argv[]) {
display_clear(display);
signal(SIGWINCH, resized);
- register_global_commands(&commands, terminate);
- register_buffer_commands(&commands);
- register_window_commands(&commands);
- register_settings_commands(&commands);
-
struct keyboard kbd = keyboard_create(reactor);
- struct keymap *current_keymap = NULL;
- struct keymap *global_keymap = register_bindings();
-
VEC_INIT(&g_watched_files, 32);
struct buffers buflist = {0};
@@ -234,7 +227,6 @@ int main(int argc, char *argv[]) {
struct buffer *ib = buffers_add(&buflist, initial_buffer);
struct buffer minibuffer = buffer_create("minibuffer");
minibuffer_init(&minibuffer);
- reset_minibuffer_keys(&minibuffer);
windows_init(display_height(display), display_width(display), ib,
&minibuffer);
@@ -249,6 +241,16 @@ int main(int argc, char *argv[]) {
buffer_view_goto(window_buffer_view(active), to);
}
+ register_global_commands(&commands, terminate);
+ register_buffer_commands(&commands);
+ register_window_commands(&commands);
+ register_settings_commands(&commands);
+
+ struct keymap *current_keymap = NULL;
+ init_bindings();
+
+ init_completion(&buflist);
+
DECLARE_TIMER(buffer);
DECLARE_TIMER(display);
DECLARE_TIMER(keyboard);
@@ -277,7 +279,7 @@ int main(int argc, char *argv[]) {
display_begin_render(display);
windows_render(display);
struct buffer_view *view = window_buffer_view(active_window);
- struct location cursor = buffer_view_dot_to_relative(view);
+ struct location cursor = buffer_view_dot_to_visual(view);
struct window_position winpos = window_position(active_window);
display_move_cursor(display, winpos.y + cursor.line, winpos.x + cursor.col);
display_end_render(display);
@@ -302,11 +304,14 @@ int main(int argc, char *argv[]) {
if (current_keymap != NULL) {
res = lookup_key(current_keymap, 1, k, &commands);
} else {
- // check the global keymap first, then the buffer one
- res = lookup_key(global_keymap, 1, k, &commands);
- if (!res.found) {
- res = lookup_key(buffer_keymap(window_buffer(active_window)), 1, k,
- &commands);
+ struct keymap *buffer_maps[128];
+ uint32_t nkeymaps =
+ buffer_keymaps(window_buffer(active_window), buffer_maps, 128);
+ for (uint32_t kmi = nkeymaps; kmi > 0; --kmi) {
+ res = lookup_key(buffer_maps[kmi - 1], 1, k, &commands);
+ if (res.found) {
+ break;
+ }
}
}
@@ -372,13 +377,14 @@ int main(int argc, char *argv[]) {
frame_allocator_clear(&frame_allocator);
}
+ destroy_completion();
windows_destroy();
minibuffer_destroy();
buffer_destroy(&minibuffer);
buffers_destroy(&buflist);
display_clear(display);
display_destroy(display);
- destroy_keymaps();
+ destroy_bindings();
command_registry_destroy(&commands);
reactor_destroy(reactor);
frame_allocator_destroy(&frame_allocator);
diff --git a/src/main/search-replace.c b/src/main/search-replace.c
index c1b812f..6467ee1 100644
--- a/src/main/search-replace.c
+++ b/src/main/search-replace.c
@@ -17,10 +17,11 @@ static struct replace {
struct region *matches;
uint32_t nmatches;
uint32_t current_match;
+ buffer_keymap_id keymap_id;
} g_current_replace = {0};
void abort_replace() {
- reset_minibuffer_keys(minibuffer_buffer());
+ buffer_remove_keymap(g_current_replace.keymap_id);
free(g_current_replace.matches);
free(g_current_replace.replace);
g_current_replace.matches = NULL;
@@ -84,7 +85,8 @@ static int32_t replace_next(struct command_ctx ctx, int argc,
.col = m->begin.col});
buffer_view_goto(buffer_view, (struct location){.line = m->end.line,
.col = m->end.col + 1});
- buffer_view_add(buffer_view, state->replace, strlen(state->replace));
+ buffer_view_add(buffer_view, (uint8_t *)state->replace,
+ strlen(state->replace));
++state->current_match;
@@ -193,14 +195,16 @@ static int32_t replace(struct command_ctx ctx, int argc, const char *argv[]) {
ANONYMOUS_BINDING(None, 'n', &skip_next_command),
ANONYMOUS_BINDING(Ctrl, 'M', &replace_next_command),
};
- buffer_bind_keys(minibuffer_buffer(), bindings,
- sizeof(bindings) / sizeof(bindings[0]));
+ struct keymap km = keymap_create("replace", 8);
+ keymap_bind_keys(&km, bindings, sizeof(bindings) / sizeof(bindings[0]));
+ g_current_replace.keymap_id = buffer_add_keymap(minibuffer_buffer(), km);
return minibuffer_prompt(ctx, "replace? [yn] ");
}
static char *g_last_search = NULL;
static bool g_last_search_interactive = false;
+static buffer_keymap_id g_search_keymap;
const char *search_prompt(bool reverse) {
const char *txt = "search (down): ";
@@ -303,7 +307,7 @@ int32_t search_interactive(struct command_ctx ctx, int argc,
} else {
struct text_chunk content = minibuffer_content();
char *p = malloc(content.nbytes + 1);
- strncpy(p, content.text, content.nbytes);
+ strncpy(p, (const char *)content.text, content.nbytes);
p[content.nbytes] = '\0';
pattern = p;
}
@@ -334,12 +338,13 @@ int32_t find(struct command_ctx ctx, int argc, const char *argv[]) {
ANONYMOUS_BINDING(Ctrl, 'S', &search_forward_command),
ANONYMOUS_BINDING(Ctrl, 'R', &search_backward_command),
};
- buffer_bind_keys(minibuffer_buffer(), bindings,
- sizeof(bindings) / sizeof(bindings[0]));
+ struct keymap m = keymap_create("search", 8);
+ keymap_bind_keys(&m, bindings, sizeof(bindings) / sizeof(bindings[0]));
+ g_search_keymap = buffer_add_keymap(minibuffer_buffer(), m);
return minibuffer_prompt(ctx, search_prompt(reverse));
}
- reset_minibuffer_keys(minibuffer_buffer());
+ buffer_remove_keymap(g_search_keymap);
if (g_last_search_interactive) {
g_last_search_interactive = false;
return 0;
@@ -347,7 +352,7 @@ int32_t find(struct command_ctx ctx, int argc, const char *argv[]) {
struct text_chunk content = minibuffer_content();
char *pattern = malloc(content.nbytes + 1);
- strncpy(pattern, content.text, content.nbytes);
+ strncpy(pattern, (const char *)content.text, content.nbytes);
pattern[content.nbytes] = '\0';
do_search(window_buffer_view(ctx.active_window), pattern, reverse);