summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlbert Cervin <albert@acervin.com>2023-05-07 23:59:22 +0200
committerAlbert Cervin <albert@acervin.com>2023-05-08 00:00:36 +0200
commit24cff0ec31970d5d57f61ba99ba7bfdda725cf94 (patch)
tree8304f69e38ec33d663db21ee26dbee14bf8d7a2f /src
parent8d73eace6806bd67852189b1a16de5895c565688 (diff)
downloaddged-24cff0ec31970d5d57f61ba99ba7bfdda725cf94.tar.gz
dged-24cff0ec31970d5d57f61ba99ba7bfdda725cf94.tar.xz
dged-24cff0ec31970d5d57f61ba99ba7bfdda725cf94.zip
Implement file reloading
When for examplue using formatters or doing vc updates, it is useful if the file is reloaded from disk.
Diffstat (limited to 'src')
-rw-r--r--src/dged/buffer.c27
-rw-r--r--src/dged/buffer.h1
-rw-r--r--src/dged/buffers.c52
-rw-r--r--src/dged/buffers.h19
-rw-r--r--src/dged/reactor-epoll.c59
-rw-r--r--src/dged/reactor.h13
-rw-r--r--src/dged/window.c12
-rw-r--r--src/main/bindings.c5
-rw-r--r--src/main/cmds.c25
-rw-r--r--src/main/main.c58
10 files changed, 235 insertions, 36 deletions
diff --git a/src/dged/buffer.c b/src/dged/buffer.c
index 958d1bb..201a75e 100644
--- a/src/dged/buffer.c
+++ b/src/dged/buffer.c
@@ -1,6 +1,5 @@
#include "buffer.h"
#include "binding.h"
-#include "bits/stdint-uintn.h"
#include "dged/vec.h"
#include "display.h"
#include "errno.h"
@@ -119,9 +118,9 @@ uint32_t buffer_add_create_hook(create_hook_cb hook, void *userdata) {
return g_num_create_hooks - 1;
}
-struct buffer buffer_create(char *name) {
+struct buffer create_internal(char *name, char *filename) {
struct buffer b = (struct buffer){
- .filename = NULL,
+ .filename = filename,
.name = strdup(name),
.text = text_create(10),
.modified = false,
@@ -132,6 +131,13 @@ struct buffer buffer_create(char *name) {
undo_init(&b.undo, 100);
+ return b;
+}
+
+struct buffer buffer_create(char *name) {
+
+ struct buffer b = create_internal(name, NULL);
+
for (uint32_t hooki = 0; hooki < g_num_create_hooks; ++hooki) {
g_create_hooks[hooki].callback(&b, g_create_hooks[hooki].userdata);
}
@@ -182,10 +188,15 @@ bool buffer_is_empty(struct buffer *buffer) {
bool buffer_is_modified(struct buffer *buffer) { return buffer->modified; }
bool buffer_is_readonly(struct buffer *buffer) { return buffer->readonly; }
+
void buffer_set_readonly(struct buffer *buffer, bool readonly) {
buffer->readonly = readonly;
}
+bool buffer_is_backed(struct buffer *buffer) {
+ return buffer->filename != NULL;
+}
+
void delete_with_undo(struct buffer *buffer, struct buffer_location start,
struct buffer_location end) {
if (buffer->readonly) {
@@ -510,9 +521,14 @@ void buffer_read_from_file(struct buffer *b) {
}
struct buffer buffer_from_file(char *filename) {
- struct buffer b = buffer_create(basename((char *)filename));
- b.filename = strdup(filename);
+ char *full_filename = realpath(filename, NULL);
+ struct buffer b = create_internal(basename((char *)filename), full_filename);
buffer_read_from_file(&b);
+
+ for (uint32_t hooki = 0; hooki < g_num_create_hooks; ++hooki) {
+ g_create_hooks[hooki].callback(&b, g_create_hooks[hooki].userdata);
+ }
+
return b;
}
@@ -812,7 +828,6 @@ void render_line(struct text_chunk *line, void *userdata) {
wchar_t wc;
size_t nbytes;
if ((nbytes = mbrtowc(&wc, (char *)txt, 6, NULL)) > 0) {
- text_nbytes += nbytes - 1;
linewidth += wcwidth(wc);
}
} else if (utf8_byte_is_ascii(*txt)) {
diff --git a/src/dged/buffer.h b/src/dged/buffer.h
index b9edf4a..05b85de 100644
--- a/src/dged/buffer.h
+++ b/src/dged/buffer.h
@@ -173,6 +173,7 @@ bool buffer_is_empty(struct buffer *buffer);
bool buffer_is_modified(struct buffer *buffer);
bool buffer_is_readonly(struct buffer *buffer);
void buffer_set_readonly(struct buffer *buffer, bool readonly);
+bool buffer_is_backed(struct buffer *buffer);
void buffer_kill_line(struct buffer_view *view);
void buffer_forward_delete_char(struct buffer_view *view);
diff --git a/src/dged/buffers.c b/src/dged/buffers.c
index 38b51b7..aa6bcea 100644
--- a/src/dged/buffers.c
+++ b/src/dged/buffers.c
@@ -2,42 +2,58 @@
#include "buffer.h"
#include <stdlib.h>
+
#include <string.h>
void buffers_init(struct buffers *buffers, uint32_t initial_capacity) {
- buffers->buffers = calloc(initial_capacity, sizeof(struct buffer));
- buffers->nbuffers = 0;
- buffers->capacity = initial_capacity;
+ VEC_INIT(&buffers->buffers, initial_capacity);
+ VEC_INIT(&buffers->add_hooks, 32);
}
struct buffer *buffers_add(struct buffers *buffers, struct buffer buffer) {
- if (buffers->nbuffers == buffers->capacity) {
- buffers->capacity *= 2;
- buffers->buffers =
- realloc(buffers->buffers, sizeof(struct buffer) * buffers->capacity);
+ VEC_PUSH(&buffers->buffers, buffer);
+
+ struct buffer *slot = VEC_BACK(&buffers->buffers);
+ VEC_FOR_EACH(&buffers->add_hooks, struct buffers_hook * hook) {
+ hook->callback(slot, hook->userdata);
}
- buffers->buffers[buffers->nbuffers] = buffer;
- ++buffers->nbuffers;
+ return slot;
+}
+
+uint32_t buffers_add_add_hook(struct buffers *buffers, buffers_hook_cb callback,
+ void *userdata) {
+ VEC_PUSH(&buffers->add_hooks, ((struct buffers_hook){
+ .callback = callback,
+ .userdata = userdata,
+ }));
- return &buffers->buffers[buffers->nbuffers - 1];
+ return VEC_SIZE(&buffers->add_hooks) - 1;
}
struct buffer *buffers_find(struct buffers *buffers, const char *name) {
- for (uint32_t bufi = 0; bufi < buffers->nbuffers; ++bufi) {
- if (strcmp(name, buffers->buffers[bufi].name) == 0) {
- return &buffers->buffers[bufi];
+ VEC_FOR_EACH(&buffers->buffers, struct buffer * b) {
+ if (strcmp(name, b->name) == 0) {
+ return b;
}
}
return NULL;
}
-void buffers_destroy(struct buffers *buffers) {
- for (uint32_t bufi = 0; bufi < buffers->nbuffers; ++bufi) {
- buffer_destroy(&buffers->buffers[bufi]);
+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;
+ }
}
- buffers->nbuffers = 0;
- free(buffers->buffers);
+ return NULL;
+}
+
+void buffers_destroy(struct buffers *buffers) {
+ VEC_FOR_EACH(&buffers->buffers, struct buffer * b) { buffer_destroy(b); }
+
+ VEC_DESTROY(&buffers->buffers);
}
diff --git a/src/dged/buffers.h b/src/dged/buffers.h
index edf772c..8add8d8 100644
--- a/src/dged/buffers.h
+++ b/src/dged/buffers.h
@@ -1,17 +1,28 @@
+#include "vec.h"
+
#include <stdint.h>
struct buffer;
+typedef void (*buffers_hook_cb)(struct buffer *buffer, void *userdata);
+
+struct buffers_hook {
+ buffers_hook_cb callback;
+ void *userdata;
+};
+
struct buffers {
- // TODO: more buffers
- struct buffer *buffers;
- uint32_t nbuffers;
- uint32_t capacity;
+ VEC(struct buffer) buffers;
+ VEC(struct buffers_hook) add_hooks;
};
void buffers_init(struct buffers *buffers, uint32_t initial_capacity);
struct buffer *buffers_add(struct buffers *buffers, struct buffer buffer);
struct buffer *buffers_find(struct buffers *buffers, const char *name);
+struct buffer *buffers_find_by_filename(struct buffers *buffers, const char *path);
+
+uint32_t buffers_add_add_hook(struct buffers *buffers, buffers_hook_cb callback, void *userdata);
+uint32_t buffers_add_remove_hook(struct buffers *buffers, buffers_hook_cb callback, void *userdata);
void buffers_destroy(struct buffers *buffers);
diff --git a/src/dged/reactor-epoll.c b/src/dged/reactor-epoll.c
index e488fef..96c6da4 100644
--- a/src/dged/reactor-epoll.c
+++ b/src/dged/reactor-epoll.c
@@ -1,12 +1,20 @@
+#include "minibuffer.h"
#include "reactor.h"
+#include <errno.h>
+#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/epoll.h>
+#include <sys/inotify.h>
+#include <unistd.h>
struct reactor {
int epoll_fd;
- void *events;
+ struct events *events;
+ int inotify_fd;
+ uint32_t inotify_poll_id;
};
struct events {
@@ -20,9 +28,16 @@ struct reactor *reactor_create() {
perror("epoll_create1");
}
+ int inotifyfd = inotify_init1(IN_NONBLOCK);
+ if (inotifyfd == -1) {
+ perror("inotify_init1");
+ }
+
struct reactor *r = (struct reactor *)calloc(1, sizeof(struct reactor));
r->epoll_fd = epollfd;
r->events = calloc(1, sizeof(struct events));
+ r->inotify_fd = inotifyfd;
+ r->inotify_poll_id = reactor_register_interest(r, inotifyfd, ReadInterest);
return r;
}
@@ -64,8 +79,48 @@ bool reactor_poll_event(struct reactor *reactor, uint32_t ev_id) {
return false;
}
+uint32_t reactor_watch_file(struct reactor *reactor, const char *path,
+ uint32_t mask) {
+ // TODO: change if we get more event types
+ mask = IN_MODIFY;
+
+ int fd = inotify_add_watch(reactor->inotify_fd, path, mask);
+ if (fd == -1) {
+ minibuffer_echo_timeout(4, "failed to watch %s: %s", path, strerror(errno));
+ return 0;
+ }
+
+ return (uint32_t)fd;
+}
+
+void reactor_unwatch_file(struct reactor *reactor, uint32_t id) {
+ inotify_rm_watch(reactor->inotify_fd, id);
+}
+
+bool reactor_next_file_event(struct reactor *reactor, struct file_event *out) {
+ if (reactor_poll_event(reactor, reactor->inotify_poll_id)) {
+ ssize_t sz = sizeof(struct inotify_event) + NAME_MAX + 1;
+ uint8_t buf[sz];
+ ssize_t bytes_read = read(reactor->inotify_fd, buf, sz);
+ if (bytes_read < 0) {
+ return false;
+ }
+
+ struct inotify_event *ev = (struct inotify_event *)buf;
+ // TODO: change when adding more of these
+ out->mask = FileWritten;
+ if (ev->mask & IN_IGNORED != 0) {
+ out->mask |= LastEvent;
+ }
+ out->id = (uint32_t)ev->wd;
+ return true;
+ }
+
+ return false;
+}
+
void reactor_update(struct reactor *reactor) {
- struct events *events = (struct events *)reactor->events;
+ struct events *events = reactor->events;
int nfds = epoll_wait(reactor->epoll_fd, events->events, 10, -1);
if (nfds == -1) {
diff --git a/src/dged/reactor.h b/src/dged/reactor.h
index e54afda..1fa014d 100644
--- a/src/dged/reactor.h
+++ b/src/dged/reactor.h
@@ -6,6 +6,16 @@ enum interest {
WriteInterest = 2,
};
+enum file_event_type {
+ FileWritten = 1 << 0,
+ LastEvent = 1 << 1,
+};
+
+struct file_event {
+ uint32_t mask;
+ uint32_t id;
+};
+
struct reactor;
struct reactor *reactor_create();
@@ -14,4 +24,7 @@ void reactor_update(struct reactor *reactor);
bool reactor_poll_event(struct reactor *reactor, uint32_t ev_id);
uint32_t reactor_register_interest(struct reactor *reactor, int fd,
enum interest interest);
+uint32_t reactor_watch_file(struct reactor *reactor, const char *path, uint32_t mask);
+void reactor_unwatch_file(struct reactor *reactor, uint32_t id);
+bool reactor_next_file_event(struct reactor *reactor, struct file_event *out);
void reactor_unregister_interest(struct reactor *reactor, uint32_t ev_id);
diff --git a/src/dged/window.c b/src/dged/window.c
index f24997c..efdcd29 100644
--- a/src/dged/window.c
+++ b/src/dged/window.c
@@ -5,6 +5,8 @@
#include "display.h"
#include "minibuffer.h"
+#include <math.h>
+
enum window_type {
Window_Buffer,
Window_HSplit,
@@ -96,8 +98,8 @@ static void window_tree_resize(struct window_node *root, uint32_t height,
BINTREE_PARENT(root) = NULL;
struct window *root_window = &BINTREE_VALUE(root);
- uint32_t width_ratio_percent = (width * 100) / (root_window->width);
- uint32_t height_ratio_percent = (height * 100) / (root_window->height);
+ uint32_t original_root_width = root_window->width;
+ uint32_t original_root_height = root_window->height;
root_window->width = width;
root_window->height = height;
@@ -108,8 +110,10 @@ static void window_tree_resize(struct window_node *root, uint32_t height,
if (BINTREE_PARENT(n) != NULL && n != root) {
if (BINTREE_LEFT(BINTREE_PARENT(n)) == n) {
// if left child, use scale from root
- w->width = (width_ratio_percent * w->width) / 100;
- w->height = (height_ratio_percent * w->height) / 100;
+ w->width = round(((float)w->width / (float)original_root_width) *
+ (float)root_window->width);
+ w->height = round(((float)w->height / (float)original_root_height) *
+ (float)root_window->height);
} else {
// if right child, fill rest of space after left and parent resize
struct window *left_sibling =
diff --git a/src/main/bindings.c b/src/main/bindings.c
index 10436d6..7ea75eb 100644
--- a/src/main/bindings.c
+++ b/src/main/bindings.c
@@ -35,11 +35,14 @@ void set_default_buffer_bindings(struct keymap *keymap) {
BINDING(Ctrl, 'S', "find-next"),
BINDING(Ctrl, 'R', "find-prev"),
+ BINDING(Meta, 'g', "goto-line"),
BINDING(Meta, '<', "goto-beginning"),
BINDING(Meta, '>', "goto-end"),
BINDING(Ctrl, 'V', "scroll-down"),
BINDING(Meta, 'v', "scroll-up"),
+ BINDING(Spec, '6', "scroll-down"),
+ BINDING(Spec, '5', "scroll-up"),
BINDING(ENTER, "newline"),
BINDING(TAB, "indent"),
@@ -204,4 +207,6 @@ void destroy_keymaps() {
km->active = false;
}
}
+
+ VEC_DESTROY(&g_buffer_keymaps);
}
diff --git a/src/main/cmds.c b/src/main/cmds.c
index 27b2a77..4cbb00f 100644
--- a/src/main/cmds.c
+++ b/src/main/cmds.c
@@ -47,8 +47,16 @@ int32_t find_file(struct command_ctx ctx, int argc, const char *argv[]) {
return 1;
}
- window_set_buffer(ctx.active_window,
- buffers_add(ctx.buffers, buffer_from_file((char *)pth)));
+ const char *filename = realpath(pth, NULL);
+ 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);
@@ -317,12 +325,14 @@ BUFFER_WRAPCMD_POS(buffer_paste_older);
BUFFER_WRAPCMD_POS(buffer_goto_beginning);
BUFFER_WRAPCMD_POS(buffer_goto_end);
BUFFER_WRAPCMD_POS(buffer_undo);
+
static int32_t buffer_view_scroll_up_cmd(struct command_ctx ctx, int argc,
const char *argv[]) {
buffer_view_scroll_up(window_buffer_view(ctx.active_window),
window_height(ctx.active_window));
return 0;
};
+
static int32_t buffer_view_scroll_down_cmd(struct command_ctx ctx, int argc,
const char *argv[]) {
buffer_view_scroll_down(window_buffer_view(ctx.active_window),
@@ -330,6 +340,16 @@ static int32_t buffer_view_scroll_down_cmd(struct command_ctx ctx, int argc,
return 0;
};
+static int32_t buffer_goto_line(struct command_ctx ctx, int argc,
+ const char *argv[]) {
+ if (argc == 0) {
+ return minibuffer_prompt(ctx, "line: ");
+ }
+
+ uint32_t line = atoi(argv[0]);
+ buffer_goto(window_buffer_view(ctx.active_window), line - 1, 0);
+}
+
void register_buffer_commands(struct commands *commands) {
static struct command buffer_commands[] = {
@@ -359,6 +379,7 @@ void register_buffer_commands(struct commands *commands) {
{.name = "scroll-down", .fn = buffer_view_scroll_down_cmd},
{.name = "scroll-up", .fn = buffer_view_scroll_up_cmd},
{.name = "reload", .fn = buffer_reload_cmd},
+ {.name = "goto-line", .fn = buffer_goto_line},
};
register_commands(commands, buffer_commands,
diff --git a/src/main/main.c b/src/main/main.c
index f13e77e..cd36733 100644
--- a/src/main/main.c
+++ b/src/main/main.c
@@ -59,6 +59,59 @@ uint64_t calc_frame_time_ns(struct timespec *timers, uint32_t num_timer_pairs) {
#define TIMED_SCOPE_BEGIN(timer) clock_gettime(CLOCK_MONOTONIC, &timer##_begin)
#define TIMED_SCOPE_END(timer) clock_gettime(CLOCK_MONOTONIC, &timer##_end)
+struct watched_file {
+ uint32_t watch_id;
+ struct buffer *buffer;
+};
+
+VEC(struct watched_file) g_watched_files;
+
+void watch_file(struct buffer *buffer, void *userdata) {
+ if (buffer_is_backed(buffer)) {
+ struct reactor *reactor = (struct reactor *)userdata;
+ VEC_APPEND(&g_watched_files, struct watched_file * w);
+ w->buffer = buffer;
+ w->watch_id = reactor_watch_file(reactor, buffer->filename, FileWritten);
+ }
+}
+
+void reload_buffer(struct buffer *buffer) {
+ if (!buffer_is_modified(buffer)) {
+ buffer_reload(buffer);
+ } else {
+ minibuffer_echo("not updating buffer %s because it contains changes",
+ buffer->name);
+ }
+}
+
+void update_file_watches(struct reactor *reactor) {
+ // first, find invalid file watches and try to update them
+ VEC_FOR_EACH(&g_watched_files, struct watched_file * w) {
+ if (w->watch_id == -1) {
+ w->watch_id =
+ reactor_watch_file(reactor, w->buffer->filename, FileWritten);
+ reload_buffer(w->buffer);
+ }
+ }
+
+ // then pick up any events we might have
+ struct file_event ev;
+ while (reactor_next_file_event(reactor, &ev)) {
+ // 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) {
+ w->watch_id = -1;
+ continue;
+ }
+
+ reload_buffer(w->buffer);
+ break;
+ }
+ }
+ }
+}
+
void usage() { printf("TODO: print usage\n"); }
int main(int argc, char *argv[]) {
@@ -127,8 +180,11 @@ int main(int argc, char *argv[]) {
struct keymap *current_keymap = NULL;
struct keymap *global_keymap = register_bindings();
+ VEC_INIT(&g_watched_files, 32);
+
struct buffers buflist = {0};
buffers_init(&buflist, 32);
+ buffers_add_add_hook(&buflist, watch_file, (void *)reactor);
struct buffer initial_buffer = buffer_create("welcome");
if (filename != NULL) {
buffer_destroy(&initial_buffer);
@@ -270,6 +326,8 @@ int main(int argc, char *argv[]) {
}
TIMED_SCOPE_END(keyboard);
+ update_file_watches(reactor);
+
// calculate frame time
struct timespec timers[] = {buffer_begin, buffer_end, display_begin,
display_end, keyboard_begin, keyboard_end};