From 35ce572bf4333c12a6a578f72d7839042e6e273f Mon Sep 17 00:00:00 2001 From: pommicket Date: Sun, 6 Aug 2023 22:14:51 -0400 Subject: buffer edit notifications --- buffer.c | 41 ++++++++++++++++++++++++++++++++++++++--- main.c | 1 + ted-internal.h | 5 +++++ ted.h | 20 +++++++++++++++++++- 4 files changed, 63 insertions(+), 4 deletions(-) diff --git a/buffer.c b/buffer.c index 36f094d..efed7fe 100644 --- a/buffer.c +++ b/buffer.c @@ -24,6 +24,12 @@ struct BufferEdit { double time; // time at start of edit (i.e. the time just before the edit), in seconds since epoch }; +struct EditNotifyInfo { + EditNotify fn; + void *context; + EditNotifyID id; +}; + // this is a macro so we get -Wformat warnings #define buffer_error(buffer, ...) \ snprintf(buffer->error, sizeof buffer->error - 1, __VA_ARGS__) @@ -761,8 +767,6 @@ static void buffer_line_free(Line *line) { free(line->str); } -// Free a buffer. Once a buffer is freed, you can call buffer_create on it again. -// Does not free the pointer `buffer` (buffer might not have even been allocated with malloc) void buffer_free(TextBuffer *buffer) { if (!buffer->ted->quit) { // don't send didClose on quit (calling buffer_lsp would actually create a LSP if this is called after destroying all the LSPs which isnt good) @@ -787,10 +791,10 @@ void buffer_free(TextBuffer *buffer) { arr_free(buffer->undo_history); arr_free(buffer->redo_history); + arr_free(buffer->edit_notifys); memset(buffer, 0, sizeof *buffer); } -// clear contents, undo history, etc. of a buffer void buffer_clear(TextBuffer *buffer) { bool is_line_buffer = buffer->is_line_buffer; Ted *ted = buffer->ted; @@ -1613,6 +1617,8 @@ BufferPos buffer_insert_text_at_pos(TextBuffer *buffer, BufferPos pos, String32 return pos; } + const u32 insertion_len = (u32)str.len; + // create a copy of str. we need to do this to remove carriage returns and newlines in the case of line buffers char32_t str_copy[256]; char32_t *str_alloc = NULL; @@ -1734,6 +1740,9 @@ BufferPos buffer_insert_text_at_pos(TextBuffer *buffer, BufferPos pos, String32 free(str_alloc); signature_help_retrigger(buffer->ted); + arr_foreach_ptr(buffer->edit_notifys, EditNotifyInfo, n) { + n->fn(buffer, n->context, pos, 0, insertion_len); + } return b; } @@ -2103,6 +2112,8 @@ void buffer_delete_chars_at_pos(TextBuffer *buffer, BufferPos pos, i64 nchars_) // Not doing this might also cause other bugs, best to keep it here just in case. nchars = (u32)buffer_get_text_at_pos(buffer, pos, NULL, nchars); + const u32 deletion_len = nchars; + if (autocomplete_is_open(buffer->ted)) { // close completions if a non-word character is deleted bool close_completions = false; @@ -2242,6 +2253,10 @@ void buffer_delete_chars_at_pos(TextBuffer *buffer, BufferPos pos, i64 nchars_) buffer_lines_modified(buffer, line_idx, line_idx); signature_help_retrigger(buffer->ted); + + arr_foreach_ptr(buffer->edit_notifys, EditNotifyInfo, info) { + info->fn(buffer, info->context, pos, deletion_len, 0); + } } // Delete characters between the given buffer positions. Returns number of characters deleted. @@ -3504,3 +3519,23 @@ void buffer_highlight_lsp_range(TextBuffer *buffer, LSPRange range, ColorSetting gl_geometry_rect(r2, colors[COLOR_HOVER_HL]); } } + +u64 buffer_add_edit_notify(TextBuffer *buffer, EditNotify notify, void *context) { + EditNotifyInfo info = { + .fn = notify, + .context = context, + .id = ++buffer->edit_notify_id, + }; + arr_add(buffer->edit_notifys, info); + return info.id; +} + +void buffer_remove_edit_notify(TextBuffer *buffer, EditNotifyID id) { + u32 i; + for (i = 0; i < arr_len(buffer->edit_notifys); ++i) { + if (buffer->edit_notifys[i].id == id) { + arr_remove(buffer->edit_notifys, i); + break; + } + } +} diff --git a/main.c b/main.c index 3f66d5a..19d4b6c 100644 --- a/main.c +++ b/main.c @@ -781,6 +781,7 @@ int main(int argc, char **argv) { ted->mouse_state = SDL_GetMouseState(&mx, &my); ted->mouse_pos = (vec2){(float)mx, (float)my}; } + // default to arrow cursor ted->cursor = ted->cursor_arrow; if (!(ted->mouse_state & SDL_BUTTON_LMASK)) { diff --git a/ted-internal.h b/ted-internal.h index 40d87f8..fdd46ef 100644 --- a/ted-internal.h +++ b/ted-internal.h @@ -167,6 +167,8 @@ typedef struct ConfigPart ConfigPart; /// A single undoable edit to a buffer typedef struct BufferEdit BufferEdit; +typedef struct EditNotifyInfo EditNotifyInfo; + struct TextBuffer { /// NULL if this buffer is untitled or doesn't correspond to a file (e.g. line buffers) char *path; @@ -248,6 +250,9 @@ struct TextBuffer { BufferEdit *undo_history; /// dynamic array of redo history BufferEdit *redo_history; + + u64 edit_notify_id; + EditNotifyInfo *edit_notifys; }; /// an entry in a selector menu (e.g. the "open" menu) diff --git a/ted.h b/ted.h index be5fb4c..2afe7e4 100644 --- a/ted.h +++ b/ted.h @@ -281,6 +281,15 @@ typedef struct { char reserved[128]; } MenuInfo; +/// this type of callback is called right after an edit is made to the buffer. +/// +/// you will be given the number of characters deleted at the position, the number +/// of characters inserted after the position, and the context pointer +/// which was passed to \ref buffer_add_edit_notify. +typedef void (*EditNotify)(TextBuffer *buffer, void *context, BufferPos pos, u32 chars_deleted, u32 chars_inserted); + +typedef u64 EditNotifyID; + // === buffer.c === /// Returns `true` if the buffer is in view-only mode. bool buffer_is_view_only(TextBuffer *buffer); @@ -368,8 +377,11 @@ char *buffer_contents_utf8_alloc(TextBuffer *buffer); /// perform a series of checks to make sure the buffer doesn't have any invalid values void buffer_check_valid(TextBuffer *buffer); /// free all resources used by the buffer +/// +/// Once a buffer is freed, you can call buffer_create on it again. +/// Does not free the pointer `buffer` (buffer might not have even been allocated with malloc) void buffer_free(TextBuffer *buffer); -/// clear buffer contents +/// clear contents, undo history, etc. of a buffer void buffer_clear(TextBuffer *buffer); /// returns the length of the `line_number`th line (0-indexed), /// or 0 if `line_number` is out of range. @@ -630,6 +642,12 @@ int buffer_pos_cmp(BufferPos p1, BufferPos p2); /// returns "`p2 - p1`", that is, the number of characters between `p1` and `p2`, /// but negative if `p1` comes after `p2`. i64 buffer_pos_diff(TextBuffer *buffer, BufferPos p1, BufferPos p2); +/// add a \ref EditNotify callback which will be called whenever `buffer` is edited. +/// +/// returns an ID which can be used with \ref buffer_remove_edit_notify +EditNotifyID buffer_add_edit_notify(TextBuffer *buffer, EditNotify notify, void *context); +/// remove edit notify callback. if `id` is zero or invalid, no action is taken. +void buffer_remove_edit_notify(TextBuffer *buffer, EditNotifyID id); // === build.c === /// clear build errors and stop -- cgit v1.2.3