summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlbert Cervin <albert@acervin.com>2024-03-26 10:59:06 +0100
committerAlbert Cervin <albert@acervin.com>2024-03-26 10:59:06 +0100
commit1b888cc723792ec0e49c7e5aaa78c3c5486a95b9 (patch)
treeda0b4f6fcdf394b155938472a0ca0d100effb2ef /src
parent5b3234f34fd081a3fe81c95bd55f4bfc853568a5 (diff)
downloaddged-1b888cc723792ec0e49c7e5aaa78c3c5486a95b9.tar.gz
dged-1b888cc723792ec0e49c7e5aaa78c3c5486a95b9.tar.xz
dged-1b888cc723792ec0e49c7e5aaa78c3c5486a95b9.zip
Implement kill-buffer command
Can be killed with the command `kill-buffer`, the shortcut `C-x k` or from the buffer menu.
Diffstat (limited to 'src')
-rw-r--r--src/dged/buffer_view.c3
-rw-r--r--src/dged/buffers.c96
-rw-r--r--src/dged/buffers.h11
-rw-r--r--src/dged/window.c95
-rw-r--r--src/dged/window.h8
-rw-r--r--src/main/bindings.c1
-rw-r--r--src/main/cmds.c100
-rw-r--r--src/main/main.c6
8 files changed, 276 insertions, 44 deletions
diff --git a/src/dged/buffer_view.c b/src/dged/buffer_view.c
index 49d938a..0ba269c 100644
--- a/src/dged/buffer_view.c
+++ b/src/dged/buffer_view.c
@@ -70,7 +70,10 @@ void buffer_view_destroy(struct buffer_view *view) {
if (view->modeline != NULL) {
free(view->modeline->buffer);
free(view->modeline);
+ view->modeline = NULL;
}
+
+ view->buffer = NULL;
}
void buffer_view_add(struct buffer_view *view, uint8_t *txt, uint32_t nbytes) {
diff --git a/src/dged/buffers.c b/src/dged/buffers.c
index 99dad9a..f591385 100644
--- a/src/dged/buffers.c
+++ b/src/dged/buffers.c
@@ -1,24 +1,42 @@
#include "buffers.h"
#include "buffer.h"
+#include <stdbool.h>
#include <stdlib.h>
-
#include <string.h>
+struct buffer_entry {
+ struct buffer buffer;
+ bool empty;
+};
+
void buffers_init(struct buffers *buffers, uint32_t initial_capacity) {
VEC_INIT(&buffers->buffers, initial_capacity);
VEC_INIT(&buffers->add_hooks, 32);
+ VEC_INIT(&buffers->remove_hooks, 32);
}
struct buffer *buffers_add(struct buffers *buffers, struct buffer buffer) {
- VEC_PUSH(&buffers->buffers, buffer);
+ struct buffer_entry *slot = NULL;
+ VEC_FOR_EACH(&buffers->buffers, struct buffer_entry * e) {
+ if (e->empty) {
+ slot = e;
+ }
+ }
+
+ if (slot == NULL) {
+ VEC_APPEND(&buffers->buffers, struct buffer_entry * new);
+ slot = new;
+ }
+
+ slot->buffer = buffer;
+ slot->empty = false;
- struct buffer *slot = VEC_BACK(&buffers->buffers);
VEC_FOR_EACH(&buffers->add_hooks, struct buffers_hook * hook) {
- hook->callback(slot, hook->userdata);
+ hook->callback(&slot->buffer, hook->userdata);
}
- return slot;
+ return &slot->buffer;
}
uint32_t buffers_add_add_hook(struct buffers *buffers, buffers_hook_cb callback,
@@ -31,10 +49,20 @@ uint32_t buffers_add_add_hook(struct buffers *buffers, buffers_hook_cb callback,
return VEC_SIZE(&buffers->add_hooks) - 1;
}
+uint32_t buffers_add_remove_hook(struct buffers *buffers,
+ buffers_hook_cb callback, void *userdata) {
+ VEC_PUSH(&buffers->remove_hooks, ((struct buffers_hook){
+ .callback = callback,
+ .userdata = userdata,
+ }));
+
+ return VEC_SIZE(&buffers->remove_hooks) - 1;
+}
+
struct buffer *buffers_find(struct buffers *buffers, const char *name) {
- VEC_FOR_EACH(&buffers->buffers, struct buffer * b) {
- if (strcmp(name, b->name) == 0) {
- return b;
+ VEC_FOR_EACH(&buffers->buffers, struct buffer_entry * e) {
+ if (!e->empty && strcmp(name, e->buffer.name) == 0) {
+ return &e->buffer;
}
}
@@ -43,23 +71,65 @@ struct buffer *buffers_find(struct buffers *buffers, const char *name) {
struct buffer *buffers_find_by_filename(struct buffers *buffers,
const char *path) {
- VEC_FOR_EACH(&buffers->buffers, struct buffer * b) {
- if (b->filename != NULL && strcmp(path, b->filename) == 0) {
- return b;
+ VEC_FOR_EACH(&buffers->buffers, struct buffer_entry * e) {
+ if (!e->empty && e->buffer.filename != NULL &&
+ strcmp(path, e->buffer.filename) == 0) {
+ return &e->buffer;
}
}
return NULL;
}
+bool buffers_remove(struct buffers *buffers, const char *name) {
+ struct buffer_entry *buf_entry = NULL;
+ VEC_FOR_EACH(&buffers->buffers, struct buffer_entry * e) {
+ if (!e->empty && strcmp(name, e->buffer.name) == 0) {
+ buf_entry = e;
+ }
+ }
+
+ if (buf_entry == NULL) {
+ return false;
+ }
+
+ VEC_FOR_EACH(&buffers->remove_hooks, struct buffers_hook * hook) {
+ hook->callback(&buf_entry->buffer, hook->userdata);
+ }
+
+ buf_entry->empty = true;
+ buffer_destroy(&buf_entry->buffer);
+ return true;
+}
+
void buffers_for_each(struct buffers *buffers, buffers_hook_cb callback,
void *userdata) {
- VEC_FOR_EACH(&buffers->buffers, struct buffer * b) { callback(b, userdata); }
+ VEC_FOR_EACH(&buffers->buffers, struct buffer_entry * e) {
+ if (!e->empty) {
+ callback(&e->buffer, userdata);
+ }
+ }
+}
+
+uint32_t buffers_num_buffers(struct buffers *buffers) {
+ return VEC_SIZE(&buffers->buffers);
+}
+
+struct buffer *buffers_first(struct buffers *buffers) {
+ return buffers_num_buffers(buffers) > 0
+ ? &VEC_ENTRIES(&buffers->buffers)[0].buffer
+ : NULL;
}
void buffers_destroy(struct buffers *buffers) {
- VEC_FOR_EACH(&buffers->buffers, struct buffer * b) { buffer_destroy(b); }
+ VEC_FOR_EACH(&buffers->buffers, struct buffer_entry * e) {
+ if (!e->empty) {
+ buffer_destroy(&e->buffer);
+ e->empty = true;
+ }
+ }
VEC_DESTROY(&buffers->buffers);
VEC_DESTROY(&buffers->add_hooks);
+ VEC_DESTROY(&buffers->remove_hooks);
}
diff --git a/src/dged/buffers.h b/src/dged/buffers.h
index f4c0a37..d521a78 100644
--- a/src/dged/buffers.h
+++ b/src/dged/buffers.h
@@ -1,5 +1,6 @@
#include "vec.h"
+#include <stdbool.h>
#include <stdint.h>
struct buffer;
@@ -11,9 +12,12 @@ struct buffers_hook {
void *userdata;
};
+struct buffer_entry;
+
struct buffers {
- VEC(struct buffer) buffers;
+ VEC(struct buffer_entry) buffers;
VEC(struct buffers_hook) add_hooks;
+ VEC(struct buffers_hook) remove_hooks;
};
void buffers_init(struct buffers *buffers, uint32_t initial_capacity);
@@ -23,6 +27,8 @@ struct buffer *buffers_find(struct buffers *buffers, const char *name);
struct buffer *buffers_find_by_filename(struct buffers *buffers,
const char *path);
+bool buffers_remove(struct buffers *buffers, const char *name);
+
uint32_t buffers_add_add_hook(struct buffers *buffers, buffers_hook_cb callback,
void *userdata);
uint32_t buffers_add_remove_hook(struct buffers *buffers,
@@ -31,4 +37,7 @@ uint32_t buffers_add_remove_hook(struct buffers *buffers,
void buffers_for_each(struct buffers *buffers, buffers_hook_cb callback,
void *userdata);
+uint32_t buffers_num_buffers(struct buffers *buffers);
+struct buffer *buffers_first(struct buffers *buffers);
+
void buffers_destroy(struct buffers *buffers);
diff --git a/src/dged/window.c b/src/dged/window.c
index 8fcd51c..20cb22f 100644
--- a/src/dged/window.c
+++ b/src/dged/window.c
@@ -2,6 +2,7 @@
#include "btree.h"
#include "buffer.h"
#include "buffer_view.h"
+#include "buffers.h"
#include "command.h"
#include "display.h"
#include "minibuffer.h"
@@ -21,7 +22,8 @@ struct window {
uint32_t height;
enum window_type type;
struct buffer_view buffer_view;
- struct buffer *prev_buffer;
+ struct buffer_view prev_buffer_view;
+ bool has_prev_buffer_view;
struct command_list *commands;
uint32_t relline;
uint32_t relcol;
@@ -40,13 +42,52 @@ static struct window g_minibuffer_window;
static struct window g_popup_window = {0};
static bool g_popup_visible = false;
+static void buffer_removed(struct buffer *buffer, void *userdata) {
+ struct window_node *n = BINTREE_ROOT(&g_windows.windows);
+ BINTREE_FIRST(n);
+ while (n != NULL) {
+ struct window *w = &BINTREE_VALUE(n);
+ if (window_prev_buffer_view(w)->buffer == buffer) {
+ buffer_view_destroy(&w->prev_buffer_view);
+ w->has_prev_buffer_view = false;
+ }
+
+ if (window_buffer(w) == buffer) {
+ if (window_has_prev_buffer_view(w)) {
+ window_set_buffer(w, window_prev_buffer_view(w)->buffer);
+ buffer_view_destroy(&w->prev_buffer_view);
+ w->has_prev_buffer_view = false;
+ } else {
+ struct buffers *buffers = (struct buffers *)userdata;
+ struct buffer *b = buffers_find(buffers, "*messages*");
+
+ if (b != NULL) {
+ window_set_buffer(w, b);
+ } else {
+ b = buffers_first(buffers);
+
+ // if there are no more buffers, not sure what to do?
+ if (b != NULL) {
+ window_set_buffer(w, b);
+ }
+ }
+ buffer_view_destroy(&w->prev_buffer_view);
+ w->has_prev_buffer_view = false;
+ }
+ }
+
+ BINTREE_NEXT(n);
+ }
+}
+
void windows_init(uint32_t height, uint32_t width,
- struct buffer *initial_buffer, struct buffer *minibuffer) {
+ struct buffer *initial_buffer, struct buffer *minibuffer,
+ struct buffers *buffers) {
BINTREE_INIT(&g_windows.windows);
g_minibuffer_window = (struct window){
.buffer_view = buffer_view_create(minibuffer, false, false),
- .prev_buffer = NULL,
+ .has_prev_buffer_view = false,
.x = 0,
.y = height - 1,
.height = 1,
@@ -55,7 +96,7 @@ void windows_init(uint32_t height, uint32_t width,
struct window root_window = (struct window){
.buffer_view = buffer_view_create(initial_buffer, true, true),
- .prev_buffer = NULL,
+ .has_prev_buffer_view = false,
.height = height - 1,
.width = width,
.x = 0,
@@ -63,6 +104,8 @@ void windows_init(uint32_t height, uint32_t width,
};
BINTREE_SET_ROOT(&g_windows.windows, root_window);
g_windows.active = &BINTREE_VALUE(BINTREE_ROOT(&g_windows.windows));
+
+ buffers_add_remove_hook(buffers, buffer_removed, buffers);
}
static void window_tree_clear_sub(struct window_node *root_node) {
@@ -72,6 +115,9 @@ static void window_tree_clear_sub(struct window_node *root_node) {
struct window *w = &BINTREE_VALUE(n);
if (w->type == Window_Buffer) {
buffer_view_destroy(&w->buffer_view);
+ if (w->has_prev_buffer_view) {
+ buffer_view_destroy(&w->prev_buffer_view);
+ }
}
BINTREE_NEXT(n);
}
@@ -377,8 +423,23 @@ void window_set_buffer(struct window *window, struct buffer *buffer) {
void window_set_buffer_e(struct window *window, struct buffer *buffer,
bool modeline, bool line_numbers) {
if (buffer != window->buffer_view.buffer) {
- window->prev_buffer = window->buffer_view.buffer;
- buffer_view_destroy(&window->buffer_view);
+ if (window->has_prev_buffer_view) {
+
+ if (window->prev_buffer_view.buffer == buffer) {
+
+ struct buffer_view tmp = window->prev_buffer_view;
+ window->prev_buffer_view = window->buffer_view;
+ window->has_prev_buffer_view = true;
+ window->buffer_view = tmp;
+ return;
+
+ } else {
+ buffer_view_destroy(&window->prev_buffer_view);
+ }
+ }
+
+ window->prev_buffer_view = window->buffer_view;
+ window->has_prev_buffer_view = true;
window->buffer_view = buffer_view_create(buffer, modeline, line_numbers);
}
}
@@ -391,12 +452,12 @@ struct buffer_view *window_buffer_view(struct window *window) {
return &window->buffer_view;
}
-struct buffer *window_prev_buffer(struct window *window) {
- return window->prev_buffer;
+struct buffer_view *window_prev_buffer_view(struct window *window) {
+ return &window->prev_buffer_view;
}
-bool window_has_prev_buffer(struct window *window) {
- return window->prev_buffer != NULL;
+bool window_has_prev_buffer_view(struct window *window) {
+ return window->has_prev_buffer_view;
}
void window_close(struct window *window) {
@@ -417,6 +478,7 @@ void window_close(struct window *window) {
: BINTREE_RIGHT(target);
buffer_view_destroy(&window->buffer_view);
+ buffer_view_destroy(&window->prev_buffer_view);
BINTREE_REMOVE(to_delete);
BINTREE_FREE_NODE(to_delete);
@@ -454,6 +516,11 @@ void window_close_others(struct window *window) {
new_root.x = 0;
new_root.y = 0;
new_root.buffer_view = buffer_view_clone(&window->buffer_view);
+ new_root.has_prev_buffer_view = false;
+ if (window->has_prev_buffer_view) {
+ new_root.has_prev_buffer_view = true;
+ new_root.prev_buffer_view = buffer_view_clone(&window->prev_buffer_view);
+ }
new_root.width = BINTREE_VALUE(root).width;
new_root.height = BINTREE_VALUE(root).height;
@@ -495,7 +562,9 @@ void window_hsplit(struct window *window, struct window **new_window_a,
buffer_view_goto(&new_window.buffer_view,
(struct location){.line = w.buffer_view.dot.line,
.col = w.buffer_view.dot.col});
- new_window.prev_buffer = w.prev_buffer;
+ if (w.has_prev_buffer_view) {
+ new_window.prev_buffer_view = buffer_view_clone(&w.prev_buffer_view);
+ }
new_window.x = w.x;
new_window.y = w.y + w.height;
new_window.width = w.width;
@@ -539,7 +608,9 @@ void window_vsplit(struct window *window, struct window **new_window_a,
(struct location){.line = w.buffer_view.dot.line,
.col = w.buffer_view.dot.col});
- new_window.prev_buffer = w.prev_buffer;
+ if (w.has_prev_buffer_view) {
+ new_window.prev_buffer_view = buffer_view_clone(&w.prev_buffer_view);
+ }
new_window.x = w.x + w.width;
new_window.y = w.y;
new_window.width = parent.width - w.width;
diff --git a/src/dged/window.h b/src/dged/window.h
index 3841475..e1b1d25 100644
--- a/src/dged/window.h
+++ b/src/dged/window.h
@@ -11,6 +11,7 @@ struct display;
struct keymap;
struct commands;
struct buffer;
+struct buffers;
struct window;
struct windows;
@@ -21,7 +22,8 @@ struct window_position {
};
void windows_init(uint32_t height, uint32_t width,
- struct buffer *initial_buffer, struct buffer *minibuffer);
+ struct buffer *initial_buffer, struct buffer *minibuffer,
+ struct buffers *buffers);
void windows_destroy();
void windows_resize(uint32_t height, uint32_t width);
@@ -44,8 +46,8 @@ void window_set_buffer_e(struct window *window, struct buffer *buffer,
bool modeline, bool line_numbers);
struct buffer *window_buffer(struct window *window);
struct buffer_view *window_buffer_view(struct window *window);
-struct buffer *window_prev_buffer(struct window *window);
-bool window_has_prev_buffer(struct window *window);
+struct buffer_view *window_prev_buffer_view(struct window *window);
+bool window_has_prev_buffer_view(struct window *window);
uint32_t window_width(const struct window *window);
uint32_t window_height(const struct window *window);
struct window_position window_position(const struct window *window);
diff --git a/src/main/bindings.c b/src/main/bindings.c
index b49a45e..7b25c7b 100644
--- a/src/main/bindings.c
+++ b/src/main/bindings.c
@@ -100,6 +100,7 @@ void init_bindings() {
BINDING(Ctrl, 'G', "find-file-relative"),
BINDING(Ctrl, 'W', "write-file"),
BINDING(None, 'b', "switch-buffer"),
+ BINDING(None, 'k', "kill-buffer"),
BINDING(Ctrl, 'B', "buffer-list"),
BINDING(None, '0', "window-close"),
diff --git a/src/main/cmds.c b/src/main/cmds.c
index c668116..d28924b 100644
--- a/src/main/cmds.c
+++ b/src/main/cmds.c
@@ -74,8 +74,8 @@ int32_t do_switch_buffer(struct command_ctx ctx, int argc, const char *argv[]) {
const char *bufname = NULL;
if (argc == 0) {
// switch back to prev buffer
- if (window_has_prev_buffer(ctx.active_window)) {
- bufname = window_prev_buffer(ctx.active_window)->name;
+ if (window_has_prev_buffer_view(ctx.active_window)) {
+ bufname = window_prev_buffer_view(ctx.active_window)->buffer->name;
} else {
return 0;
}
@@ -110,9 +110,10 @@ int32_t switch_buffer(struct command_ctx ctx, int argc, const char *argv[]) {
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): ",
- window_prev_buffer(ctx.active_window)->name);
+ if (window_has_prev_buffer_view(ctx.active_window)) {
+ return minibuffer_prompt(
+ ctx, "buffer (default %s): ",
+ window_prev_buffer_view(ctx.active_window)->buffer->name);
} else {
return minibuffer_prompt(ctx, "buffer: ");
}
@@ -124,6 +125,51 @@ int32_t switch_buffer(struct command_ctx ctx, int argc, const char *argv[]) {
ctx.active_window, ctx.buffers, argc, argv);
}
+int32_t do_kill_buffer(struct command_ctx ctx, int argc, const char *argv[]) {
+ disable_completion(minibuffer_buffer());
+ const char *bufname = NULL;
+ if (argc == 0) {
+ // kill current buffer
+ bufname = window_buffer(ctx.active_window)->name;
+ } else {
+ bufname = argv[0];
+ }
+
+ if (buffers_remove(ctx.buffers, bufname)) {
+ return 0;
+ } else {
+ minibuffer_echo_timeout(4, "buffer %s not found", bufname);
+ return 1;
+ }
+}
+
+COMMAND_FN("do-kill-buffer", do_kill_buffer, do_kill_buffer, NULL);
+
+static void kill_buffer_comp_inserted() { minibuffer_execute(); }
+
+int32_t kill_buffer(struct command_ctx ctx, int argc, const char *argv[]) {
+ if (argc == 0) {
+ minibuffer_clear();
+ struct completion_provider providers[] = {buffer_provider()};
+ enable_completion(minibuffer_buffer(),
+ ((struct completion_trigger){
+ .kind = CompletionTrigger_Input,
+ .input =
+ (struct completion_trigger_input){
+ .nchars = 0, .trigger_initially = false}}),
+ providers, 1, kill_buffer_comp_inserted);
+
+ ctx.self = &do_kill_buffer_command;
+ return minibuffer_prompt(ctx, "kill buffer (default %s): ",
+ window_buffer(ctx.active_window)->name);
+ }
+
+ disable_completion(minibuffer_buffer());
+
+ return execute_command(&do_switch_buffer_command, ctx.commands,
+ ctx.active_window, ctx.buffers, argc, argv);
+}
+
void timer_to_list_line(const struct timer *timer, void *userdata) {
struct buffer *target = (struct buffer *)userdata;
@@ -241,6 +287,34 @@ int32_t buflist_refresh_cmd(struct command_ctx ctx, int argc,
return 0;
}
+static struct command buflist_refresh_command = {
+ .name = "buflist-refresh",
+ .fn = buflist_refresh_cmd,
+};
+
+int32_t buflist_kill_cmd(struct command_ctx ctx, int argc, const char *argv[]) {
+ struct window *w = ctx.active_window;
+
+ struct buffer_view *bv = window_buffer_view(w);
+ struct text_chunk text = buffer_line(bv->buffer, bv->dot.line);
+
+ char *end = (char *)memchr(text.text, ' ', text.nbytes);
+
+ if (end != NULL) {
+ uint32_t len = end - (char *)text.text;
+ char *bufname = (char *)malloc(len + 1);
+ strncpy(bufname, (const char *)text.text, len);
+ bufname[len] = '\0';
+
+ buffers_remove(ctx.buffers, bufname);
+ free(bufname);
+ execute_command(&buflist_refresh_command, ctx.commands, ctx.active_window,
+ ctx.buffers, 0, NULL);
+ }
+
+ return 0;
+}
+
int32_t buffer_list(struct command_ctx ctx, int argc, const char *argv[]) {
struct buffer *b = buffers_find(ctx.buffers, "*buffers*");
if (b == NULL) {
@@ -258,20 +332,21 @@ int32_t buffer_list(struct command_ctx ctx, int argc, const char *argv[]) {
.fn = buflist_visit_cmd,
};
+ static struct command buflist_kill = {
+ .name = "buflist-kill",
+ .fn = buflist_kill_cmd,
+ };
+
static struct command buflist_close = {
.name = "buflist-close",
.fn = buflist_close_cmd,
};
- static struct command buflist_refresh = {
- .name = "buflist-refresh",
- .fn = buflist_refresh_cmd,
- };
-
struct binding bindings[] = {
- ANONYMOUS_BINDING(Ctrl, 'M', &buflist_visit),
+ ANONYMOUS_BINDING(ENTER, &buflist_visit),
+ ANONYMOUS_BINDING(None, 'k', &buflist_kill),
ANONYMOUS_BINDING(None, 'q', &buflist_close),
- ANONYMOUS_BINDING(None, 'g', &buflist_refresh),
+ ANONYMOUS_BINDING(None, 'g', &buflist_refresh_command),
};
struct keymap km = keymap_create("buflist", 8);
keymap_bind_keys(&km, bindings, sizeof(bindings) / sizeof(bindings[0]));
@@ -396,6 +471,7 @@ void register_global_commands(struct commands *commands,
{.name = "write-file", .fn = write_file},
{.name = "run-command-interactive", .fn = run_interactive},
{.name = "switch-buffer", .fn = switch_buffer},
+ {.name = "kill-buffer", .fn = kill_buffer},
{.name = "abort", .fn = _abort},
{.name = "timers", .fn = timers},
{.name = "buffer-list", .fn = buffer_list},
diff --git a/src/main/main.c b/src/main/main.c
index 927785e..6048b63 100644
--- a/src/main/main.c
+++ b/src/main/main.c
@@ -61,7 +61,7 @@ void segfault() {
display_destroy(display);
}
- printf("Segfault encountered...\n");
+ fprintf(stderr, "Segfault encountered...\n");
abort();
}
@@ -289,8 +289,8 @@ int main(int argc, char *argv[]) {
struct buffer *ib = buffers_add(&buflist, initial_buffer);
- windows_init(display_height(display), display_width(display), ib,
- &minibuffer);
+ windows_init(display_height(display), display_width(display), ib, &minibuffer,
+ &buflist);
struct window *active = windows_get_active();
if (goto_end) {
buffer_view_goto_end(window_buffer_view(active));