summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2023-08-06 22:14:51 -0400
committerpommicket <pommicket@gmail.com>2023-08-06 22:14:51 -0400
commit35ce572bf4333c12a6a578f72d7839042e6e273f (patch)
tree75dc03b0edd194df6ab2a3bd9daf5bd4e77f7615
parent823815101f55ca0ce67f36867b93934ccdba1953 (diff)
buffer edit notifications
-rw-r--r--buffer.c41
-rw-r--r--main.c1
-rw-r--r--ted-internal.h5
-rw-r--r--ted.h20
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