diff options
| -rw-r--r-- | Makefile | 13 | ||||
| -rw-r--r-- | linux.mk | 3 | ||||
| -rw-r--r-- | src/dged/buffer.c | 27 | ||||
| -rw-r--r-- | src/dged/buffer.h | 1 | ||||
| -rw-r--r-- | src/dged/buffers.c | 52 | ||||
| -rw-r--r-- | src/dged/buffers.h | 19 | ||||
| -rw-r--r-- | src/dged/reactor-epoll.c | 59 | ||||
| -rw-r--r-- | src/dged/reactor.h | 13 | ||||
| -rw-r--r-- | src/dged/window.c | 12 | ||||
| -rw-r--r-- | src/main/bindings.c | 5 | ||||
| -rw-r--r-- | src/main/cmds.c | 25 | ||||
| -rw-r--r-- | src/main/main.c | 58 |
12 files changed, 242 insertions, 45 deletions
@@ -23,10 +23,11 @@ prefix ?= "/usr" .SUFFIXES: .SUFFIXES: .c .o .d -UNAME_S != uname -s | tr '[:upper:]' '[:lower:]' - CFLAGS = -Werror -g -std=c99 -I $(.CURDIR)/src -I $(.CURDIR)/src/main +UNAME_S != uname -s | tr '[:upper:]' '[:lower:]' +.sinclude "$(UNAME_S).mk" + DEPS = $(DGED_SOURCES:.c=.d) $(TEST_SOURCES:.c=.d) OBJS = $(SOURCES:.c=.o) @@ -35,8 +36,6 @@ TEST_OBJS = $(TEST_SOURCES:.c=.o) FILES = $(DEPS) $(MAIN_OBJS) $(OBJS) dged libdged.a $(TEST_OBJS) -.sinclude "$(UNAME_S).mk" - # dependency generation .c.d: @mkdir -p $(@D) @@ -48,10 +47,10 @@ FILES = $(DEPS) $(MAIN_OBJS) $(OBJS) dged libdged.a $(TEST_OBJS) $(CC) $(CFLAGS) -c $< -o $@ dged: $(MAIN_OBJS) libdged.a - $(CC) $(LDFLAGS) $(MAIN_OBJS) libdged.a -o dged + $(CC) $(LDFLAGS) $(MAIN_OBJS) libdged.a -o dged -lm -libdged.a: $(OBJS) $(PLATFORM_OBJS) - $(AR) -rc libdged.a $(OBJS) $(PLATFORM_OBJS) +libdged.a: $(OBJS) + $(AR) -rc libdged.a $(OBJS) run-tests: $(TEST_OBJS) $(OBJS) $(CC) $(LDFLAGS) $(TEST_OBJS) $(OBJS) -o run-tests @@ -1,4 +1,3 @@ CFLAGS += -DLINUX -D_XOPEN_SOURCE=700 -PLATFORM_SOURCES = src/reactor-epoll.c -PLATFORM_OBJS = $(PLATFORM_SOURCES:.c=.o) +SOURCES += src/dged/reactor-epoll.c 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}; |
