summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--buffer.c6
-rw-r--r--command.c19
-rw-r--r--ds.h14
-rw-r--r--ide-rename-symbol.c17
-rw-r--r--main.c43
-rw-r--r--node.c179
-rw-r--r--session.c130
-rw-r--r--ted-internal.h32
-rw-r--r--ted.c273
-rw-r--r--ted.h25
-rw-r--r--unicode.h11
11 files changed, 369 insertions, 380 deletions
diff --git a/buffer.c b/buffer.c
index 8e1bbfb..1c33f99 100644
--- a/buffer.c
+++ b/buffer.c
@@ -804,6 +804,8 @@ static void buffer_line_free(Line *line) {
}
void buffer_free(TextBuffer *buffer) {
+ if (!buffer) return;
+
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)
LSP *lsp = buffer_lsp(buffer);
@@ -2647,6 +2649,10 @@ void buffer_paste(TextBuffer *buffer) {
// if an error occurs, buffer is left untouched (except for the error field) and the function returns false.
Status buffer_load_file(TextBuffer *buffer, const char *path) {
+ if (!unicode_is_valid_utf8(path)) {
+ buffer_error(buffer, "Path is not valid UTF8.");
+ return false;
+ }
if (!path || !path_is_absolute(path)) {
buffer_error(buffer, "Loaded path '%s' is not an absolute path.", path);
return false;
diff --git a/command.c b/command.c
index 084981a..e573049 100644
--- a/command.c
+++ b/command.c
@@ -443,17 +443,14 @@ void command_execute_ex(Ted *ted, Command c, const CommandArgument *full_argumen
ted->quit = true;
} else {
*ted->warn_unsaved_names = 0;
- bool *buffers_used = ted->buffers_used;
bool first = true;
- for (u16 i = 0; i < TED_MAX_BUFFERS; ++i) {
- if (buffers_used[i]) {
- buffer = &ted->buffers[i];
- if (buffer_unsaved_changes(buffer)) {
- const char *path = buffer_display_filename(buffer);
- strbuf_catf(ted->warn_unsaved_names, "%s%s", first ? "" : ", ", path);
- first = false;
- }
+ arr_foreach_ptr(ted->buffers, TextBufferPtr, pbuffer) {
+ buffer = *pbuffer;
+ if (buffer_unsaved_changes(buffer)) {
+ const char *path = buffer_display_filename(buffer);
+ strbuf_catf(ted->warn_unsaved_names, "%s%s", first ? "" : ", ", path);
+ first = false;
}
}
@@ -569,7 +566,7 @@ void command_execute_ex(Ted *ted, Command c, const CommandArgument *full_argumen
find_close(ted);
} else if (node) {
u16 tab_idx = node->active_tab;
- buffer = &ted->buffers[node->tabs[tab_idx]];
+ buffer = node->tabs[tab_idx];
// (an argument of 2 overrides the unsaved changes dialog)
if (argument != 2 && buffer_unsaved_changes(buffer)) {
// there are unsaved changes!
@@ -581,7 +578,7 @@ void command_execute_ex(Ted *ted, Command c, const CommandArgument *full_argumen
}
} else if (ted->build_shown) {
build_stop(ted);
- } else if (ted->nodes_used[0]) {
+ } else if (arr_len(ted->nodes)) {
// there are nodes open, but no active node.
// do nothing.
} else {
diff --git a/ds.h b/ds.h
index ffdfec8..86d8371 100644
--- a/ds.h
+++ b/ds.h
@@ -208,6 +208,18 @@ static void *arr_remove_(void *arr, size_t member_size, size_t index) {
}
}
+static i32 arr_index_of_(void *arr, size_t member_size, const void *item) {
+ if (!arr) return -1;
+
+ ArrHeader *hdr = arr_hdr_(arr);
+ for (size_t i = 0; i < hdr->len; ++i) {
+ if (memcmp((const char *)arr + i * member_size, item, member_size) == 0)
+ return (i32)i;
+ }
+
+ return -1;
+}
+
static void *arr_remove_multiple_(void *arr, size_t member_size, size_t index, size_t count) {
ArrHeader *hdr = arr_hdr_(arr);
assert(index < hdr->len);
@@ -269,6 +281,8 @@ static void *arr_copy_(const void *arr, size_t member_size) {
#define arr_qsort(a, cmp) qsort((a), arr_len(a), sizeof *(a), (cmp))
#define arr_remove_last(a) do { assert(a); if (--arr_hdr_(a)->len == 0) arr_free(a); } while (0)
#define arr_remove(a, i) (void)((a) = arr_remove_((a), sizeof *(a), (i)))
+#define arr_remove_item(a, item) do { for (u32 _i = 0; _i < arr_len((a)); ++_i) if ((a)[_i] == item) { arr_remove((a), _i); break; } } while (0);
+#define arr_index_of(a, item) (sizeof((a)[0] == (item)), arr_index_of_((a), sizeof *(a), &(item)))
#define arr_remove_multiple(a, i, n) (void)((a) = arr_remove_multiple_((a), sizeof *(a), (i), (n)))
#define arr_insert(a, i, x) do { u32 _index = (i); (a) = arr_cast_typeof(a) arr_grow1_((a), sizeof *(a)); \
if (a) { memmove((a) + _index + 1, (a) + _index, (arr_len(a) - _index) * sizeof *(a));\
diff --git a/ide-rename-symbol.c b/ide-rename-symbol.c
index 97c16a5..0ff69e8 100644
--- a/ide-rename-symbol.c
+++ b/ide-rename-symbol.c
@@ -207,18 +207,17 @@ void rename_symbol_process_lsp_response(Ted *ted, const LSPResponse *response) {
}
}
done:
-
- // end all edit chains in all buffers
- // they're almost definitely all created by us
- for (u16 i = 0; i < TED_MAX_BUFFERS; ++i) {
- if (ted->buffers_used[i]) {
- TextBuffer *buffer = &ted->buffers[i];
- buffer_end_edit_chain(buffer);
+
+ {
+ // end all edit chains in all buffers
+ // they're almost definitely all created by us
+ arr_foreach_ptr(ted->buffers, TextBufferPtr, pbuffer) {
+ buffer_end_edit_chain(*pbuffer);
}
+
+ ted_save_all(ted);
}
- ted_save_all(ted);
-
cleanup:
rename_symbol_clear(ted);
if (menu_is_open(ted, MENU_RENAME_SYMBOL))
diff --git a/main.c b/main.c
index 102290e..5f2739b 100644
--- a/main.c
+++ b/main.c
@@ -1,5 +1,6 @@
/*
TODO:
+- set limit for # of nodes/buffers/tabs
- public Node API
- public Selector/FileSelector API
@@ -683,15 +684,13 @@ int main(int argc, char **argv) {
// could switch to a different buffer.
// line buffer click handling, IS done in buffer_render (yes this is less than ideal)
if (!menu_is_any_open(ted)) {
- for (u32 i = 0; i < TED_MAX_NODES; ++i) {
- if (ted->nodes_used[i]) {
- Node *node = &ted->nodes[i];
- if (node->tabs) {
- buffer = &ted->buffers[node->tabs[node->active_tab]];
- if (buffer_handle_click(ted, buffer, pos, times)) {
- add = false;
- break;
- }
+ arr_foreach_ptr(ted->nodes, NodePtr, pnode) {
+ Node *node = *pnode;
+ if (node->tabs) {
+ buffer = node->tabs[node->active_tab];
+ if (buffer_handle_click(ted, buffer, pos, times)) {
+ add = false;
+ break;
}
}
}
@@ -966,7 +965,6 @@ int main(int argc, char **argv) {
{
const float padding = ted_active_settings(ted)->padding;
float x1 = padding, y = window_height-padding, x2 = window_width-padding;
- Node *node = &ted->nodes[0];
if (ted->find) {
float y2 = y;
y -= find_menu_height(ted);
@@ -1003,7 +1001,8 @@ int main(int argc, char **argv) {
y -= padding;
}
- if (ted->nodes_used[0]) {
+ if (arr_len(ted->nodes)) {
+ Node *node = ted->nodes[0];
float y1 = padding;
node_frame(ted, node, rect4(x1, y1, x2, y));
autocomplete_frame(ted);
@@ -1038,8 +1037,8 @@ int main(int argc, char **argv) {
if (text_has_err()) {
ted_error(ted, "Couldn't render text: %s", text_get_err());
}
- for (u16 i = 0; i < TED_MAX_BUFFERS; ++i) {
- TextBuffer *buffer = &ted->buffers[i];
+ arr_foreach_ptr(ted->buffers, TextBufferPtr, pbuffer) {
+ TextBuffer *buffer = *pbuffer;
if (buffer_has_error(buffer)) {
ted_error_from_buffer(ted, buffer);
buffer_clear_error(buffer);
@@ -1120,9 +1119,8 @@ int main(int argc, char **argv) {
ted_check_for_node_problems(ted);
#if !NDEBUG
- for (u16 i = 0; i < TED_MAX_BUFFERS; ++i)
- if (ted->buffers_used[i])
- buffer_check_valid(&ted->buffers[i]);
+ for (u16 i = 0; i < arr_len(ted->buffers); ++i)
+ buffer_check_valid(ted->buffers[i]);
buffer_check_valid(&ted->line_buffer);
#endif
@@ -1224,12 +1222,13 @@ int main(int argc, char **argv) {
SDL_GL_DeleteContext(glctx);
SDL_DestroyWindow(window);
SDL_Quit();
- for (u16 i = 0; i < TED_MAX_BUFFERS; ++i)
- if (ted->buffers_used[i])
- buffer_free(&ted->buffers[i]);
- for (u16 i = 0; i < TED_MAX_NODES; ++i)
- if (ted->nodes_used[i])
- node_free(&ted->nodes[i]);
+ for (u16 i = 0; i < arr_len(ted->buffers); ++i)
+ buffer_free(ted->buffers[i]);
+ arr_clear(ted->buffers);
+ arr_foreach_ptr(ted->nodes, NodePtr, pnode) {
+ node_free(*pnode);
+ }
+ arr_clear(ted->nodes);
buffer_free(&ted->line_buffer);
buffer_free(&ted->find_buffer);
buffer_free(&ted->replace_buffer);
diff --git a/node.c b/node.c
index 6acbfa8..51a1422 100644
--- a/node.c
+++ b/node.c
@@ -10,8 +10,8 @@ static void node_switch_to_tab(Ted *ted, Node *node, u16 new_tab_index) {
if (node == ted->active_node) {
// switch active buffer
assert(node->tabs);
- u16 buffer_idx = node->tabs[new_tab_index];
- ted_switch_to_buffer(ted, &ted->buffers[buffer_idx]);
+ TextBuffer *buffer = node->tabs[new_tab_index];
+ ted_switch_to_buffer(ted, buffer);
}
}
@@ -41,130 +41,124 @@ void node_tabs_swap(Node *node, i32 tab1i, i32 tab2i) {
u16 tab1 = (u16)tab1i, tab2 = (u16)tab2i;
if (node->active_tab == tab1) node->active_tab = tab2;
else if (node->active_tab == tab2) node->active_tab = tab1;
- u16 tmp = node->tabs[tab1];
+ TextBuffer *temp = node->tabs[tab1];
node->tabs[tab1] = node->tabs[tab2];
- node->tabs[tab2] = tmp;
+ node->tabs[tab2] = temp;
}
void node_free(Node *node) {
arr_free(node->tabs);
memset(node, 0, sizeof *node);
+ free(node);
}
-i32 node_parent(Ted *ted, i32 node_idx) {
- const bool *nodes_used = ted->nodes_used;
- if (node_idx < 0 || node_idx >= TED_MAX_NODES || !nodes_used[node_idx])
- return -1;
- for (u16 i = 0; i < TED_MAX_NODES; ++i) {
- if (nodes_used[i]) {
- Node *node = &ted->nodes[i];
- if (!node->tabs) {
- if (node->split_a == node_idx || node->split_b == node_idx)
- return i;
- }
+Node *node_parent(Ted *ted, Node *child) {
+ if (!child)
+ return NULL;
+ arr_foreach_ptr(ted->nodes, NodePtr, pnode) {
+ Node *node = *pnode;
+ if (!node->tabs) {
+ if (node->split_a == child || node->split_b == child)
+ return node;
}
}
- return -1;
+ return NULL;
}
// the root has depth 0, and a child node has 1 more than its parent's depth.
-static u8 node_depth(Ted *ted, i32 node_idx) {
+static u8 node_depth(Ted *ted, Node *node) {
u8 depth = 0;
- while (node_idx != -1) {
- node_idx = node_parent(ted, node_idx);
+ while (node) {
+ node = node_parent(ted, node);
++depth;
}
return depth;
}
void node_join(Ted *ted, Node *node) {
- i32 parent_idx = node_parent(ted, (u16)(node - ted->nodes));
- if (parent_idx >= 0) {
- Node *parent = &ted->nodes[parent_idx];
- Node *a = &ted->nodes[parent->split_a];
- Node *b = &ted->nodes[parent->split_b];
+ Node *parent = node_parent(ted, node);
+ if (parent) {
+ Node *a = parent->split_a;
+ Node *b = parent->split_b;
if (a->tabs && b->tabs) {
if (ted->active_node == a || ted->active_node == b) {
ted->active_node = parent;
}
- arr_foreach_ptr(a->tabs, u16, tab) {
+ arr_foreach_ptr(a->tabs, TextBufferPtr, tab) {
arr_add(parent->tabs, *tab);
}
- arr_foreach_ptr(b->tabs, u16, tab) {
+ arr_foreach_ptr(b->tabs, TextBufferPtr, tab) {
arr_add(parent->tabs, *tab);
}
if (!parent->tabs) {
ted_out_of_mem(ted);
+ return;
+ }
+
+ parent->split_a = NULL;
+ parent->split_b = NULL;
+ if (node == a) {
+ parent->active_tab = a->active_tab;
} else {
- if (node == a) {
- parent->active_tab = a->active_tab;
- } else {
- parent->active_tab = (u16)arr_len(a->tabs) + b->active_tab;
- }
- node_free(a);
- node_free(b);
- ted->nodes_used[parent->split_a] = false;
- ted->nodes_used[parent->split_b] = false;
- // this isn't really needed since parent->tabs is not NULL anymore.
- parent->split_a = 0;
- parent->split_b = 0;
+ parent->active_tab = (u16)arr_len(a->tabs) + b->active_tab;
}
+ node_free(a);
+ node_free(b);
+ arr_remove_item(ted->nodes, a);
+ arr_remove_item(ted->nodes, b);
+
}
}
}
-void node_close(Ted *ted, i32 node_idx) {
+void node_close(Ted *ted, Node *node) {
ted->dragging_tab_node = NULL;
ted->resizing_split = NULL;
- if (node_idx < 0 || node_idx >= TED_MAX_NODES) {
+ if (!node) {
return;
}
- if (!ted->nodes_used[node_idx]) {
- return;
- }
- i32 parent_idx = node_parent(ted, node_idx);
- ted->nodes_used[node_idx] = false;
-
- Node *node = &ted->nodes[node_idx];
+
+
+ Node *parent = node_parent(ted, node);
bool was_active = ted->active_node == node;
// delete all associated buffers
- arr_foreach_ptr(node->tabs, u16, tab) {
- u16 buffer_index = *tab;
- ted_delete_buffer(ted, buffer_index);
+ arr_foreach_ptr(node->tabs, TextBufferPtr, tab) {
+ TextBuffer *buffer = *tab;
+ ted_delete_buffer(ted, buffer);
}
node_free(node);
- if (parent_idx < 0) {
+ if (!parent) {
// no parent; this must be the root node
ted->active_node = NULL;
} else {
// turn parent from split node into tab node
- Node *parent = &ted->nodes[parent_idx];
if (parent->tabs) {
assert(0); // this node's parent should be a split node
return;
}
- u16 other_side;
- if (node_idx == parent->split_a) {
+ Node *other_side;
+ if (node == parent->split_a) {
other_side = parent->split_b;
} else {
- assert(node_idx == parent->split_b);
+ assert(node == parent->split_b);
other_side = parent->split_a;
}
// replace parent with other side of split
- *parent = ted->nodes[other_side];
-
- ted->nodes_used[other_side] = false;
+ *parent = *other_side;
+ arr_remove_item(ted->nodes, other_side);
if (was_active) {
Node *new_active_node = parent;
// make sure we don't set the active node to a split
while (!new_active_node->tabs)
- new_active_node = &ted->nodes[new_active_node->split_a];
+ new_active_node = new_active_node->split_a;
ted_node_switch(ted, new_active_node);
}
}
+
+ arr_remove_item(ted->nodes, node);
}
@@ -179,14 +173,14 @@ bool node_tab_close(Ted *ted, Node *node, i32 index) {
if (ntabs == 1) {
// only 1 tab left, just close the node
- node_close(ted, (u16)(node - ted->nodes));
+ node_close(ted, node);
return false;
} else {
bool was_active = ted->active_node == node; // ted->active_node will be set to NULL when the active buffer is deleted.
- u16 buffer_index = node->tabs[index];
+ TextBuffer *buffer = node->tabs[index];
// remove tab from array
arr_remove(node->tabs, (size_t)index);
- ted_delete_buffer(ted, buffer_index);
+ ted_delete_buffer(ted, buffer);
ntabs = (u16)arr_len(node->tabs); // update ntabs
assert(ntabs);
@@ -196,7 +190,7 @@ bool node_tab_close(Ted *ted, Node *node, i32 index) {
node->active_tab = clamp_u16(node->active_tab, 0, ntabs - 1);
if (was_active) {
// fix active buffer if necessary
- ted_switch_to_buffer(ted, &ted->buffers[node->tabs[node->active_tab]]);
+ ted_switch_to_buffer(ted, node->tabs[node->active_tab]);
}
return true;
}
@@ -240,7 +234,7 @@ void node_frame(Ted *ted, Node *node, Rect r) {
if (tab_index <= arr_len(node->tabs)) {
Node *drag_node = ted->dragging_tab_node;
u16 drag_index = ted->dragging_tab_idx;
- u16 tab = drag_node->tabs[drag_index];
+ TextBuffer *tab = drag_node->tabs[drag_index];
// remove the old tab
arr_remove(drag_node->tabs, drag_index);
@@ -253,7 +247,7 @@ void node_frame(Ted *ted, Node *node, Rect r) {
arr_insert(node->tabs, tab_index, tab);
if (arr_len(drag_node->tabs) == 0) {
// removed the last tab from a node; close it
- node_close(ted, (u16)(drag_node - ted->nodes));
+ node_close(ted, drag_node);
} else {
// make sure active tab is valid
drag_node->active_tab = clamp_u16(drag_node->active_tab, 0, (u16)arr_len(drag_node->tabs) - 1);
@@ -261,7 +255,7 @@ void node_frame(Ted *ted, Node *node, Rect r) {
ted->dragging_tab_node = NULL; // stop dragging
// switch to this buffer
- ted_switch_to_buffer(ted, &ted->buffers[tab]);
+ ted_switch_to_buffer(ted, tab);
}
}
}
@@ -271,8 +265,7 @@ void node_frame(Ted *ted, Node *node, Rect r) {
if (rect_contains_point(tab_bar_rect, click->pos)) {
u16 tab_index = (u16)((click->pos.x - r.pos.x) / tab_width);
if (tab_index < arr_len(node->tabs)) {
- u16 buffer_idx = node->tabs[tab_index];
- TextBuffer *buffer = &ted->buffers[buffer_idx];
+ TextBuffer *buffer = node->tabs[tab_index];
// close that tab
if (buffer_unsaved_changes(buffer)) {
// make sure unsaved changes dialog is opened
@@ -292,7 +285,7 @@ void node_frame(Ted *ted, Node *node, Rect r) {
TextRenderState text_state = text_render_state_default;
for (u16 i = 0; i < ntabs; ++i) {
- TextBuffer *buffer = &ted->buffers[node->tabs[i]];
+ TextBuffer *buffer = node->tabs[i];
char tab_title[256];
const char *filename = buffer_display_filename(buffer);
Rect tab_rect = rect_xywh(r.pos.x + tab_width * i, r.pos.y, tab_width, tab_bar_height);
@@ -340,9 +333,7 @@ void node_frame(Ted *ted, Node *node, Rect r) {
text_render(font);
}
- u16 buffer_index = node->tabs[node->active_tab];
- TextBuffer *buffer = &ted->buffers[buffer_index];
- assert(ted->buffers_used[buffer_index]);
+ TextBuffer *buffer = node->tabs[node->active_tab];
Rect buffer_rect = r;
buffer_rect.pos.y += tab_bar_height;
@@ -355,8 +346,7 @@ void node_frame(Ted *ted, Node *node, Rect r) {
} else {
float padding = settings->padding;
// this node is a split
- Node *a = &ted->nodes[node->split_a];
- Node *b = &ted->nodes[node->split_b];
+ Node *a = node->split_a, *b = node->split_b;
Rect r1 = r, r2 = r;
SDL_Cursor *resize_cursor = node->split_vertical ? ted->cursor_resize_v : ted->cursor_resize_h;
if (node == ted->resizing_split) {
@@ -399,14 +389,12 @@ void node_frame(Ted *ted, Node *node, Rect r) {
}
void node_split(Ted *ted, Node *node, bool vertical) {
- if (node_depth(ted, (u16)(node - ted->nodes)) >= 4) return; // prevent splitting too deep
+ if (node_depth(ted, node) >= 4) return; // prevent splitting too deep
if (arr_len(node->tabs) > 1) { // need at least 2 tabs to split
- i32 left_idx = ted_new_node(ted);
- i32 right_idx = ted_new_node(ted);
- if (left_idx >= 0 && right_idx >= 0) {
- Node *left = &ted->nodes[left_idx];
- Node *right = &ted->nodes[right_idx];
+ Node *left = ted_new_node(ted);
+ Node *right = ted_new_node(ted);
+ if (left && right) {
u16 active_tab = node->active_tab;
// put active tab on the right
arr_add(right->tabs, node->tabs[active_tab]);
@@ -418,38 +406,31 @@ void node_split(Ted *ted, Node *node, bool vertical) {
}
arr_clear(node->tabs);
- node->split_a = (u16)left_idx;
- node->split_b = (u16)right_idx;
+ node->split_a = left;
+ node->split_b = right;
node->split_vertical = vertical;
node->split_pos = 0.5f;
if (node == ted->active_node)
- ted_node_switch(ted, &ted->nodes[right_idx]);
+ ted_node_switch(ted, right);
}
}
}
void node_split_switch(Ted *ted) {
- assert(ted->active_node);
- u16 active_node_idx = (u16)(ted->active_node - ted->nodes);
- i32 parent_idx = node_parent(ted, active_node_idx);
- if (parent_idx < 0) return;
- Node *parent = &ted->nodes[parent_idx];
- if (parent) {
- if (parent->split_a == active_node_idx) {
- ted_node_switch(ted, &ted->nodes[parent->split_b]);
- } else {
- ted_node_switch(ted, &ted->nodes[parent->split_a]);
- }
+ Node *parent = node_parent(ted, ted->active_node);
+ if (!parent) return;
+ if (parent->split_a == ted->active_node) {
+ ted_node_switch(ted, parent->split_b);
+ } else {
+ ted_node_switch(ted, parent->split_a);
}
}
void node_split_swap(Ted *ted) {
assert(ted->active_node);
- u16 active_node_idx = (u16)(ted->active_node - ted->nodes);
- i32 parent_idx = node_parent(ted, active_node_idx);
- if (parent_idx < 0) return;
- Node *parent = &ted->nodes[parent_idx];
- u16 temp = parent->split_a;
+ Node *parent = node_parent(ted, ted->active_node);
+ if (!parent) return;
+ Node *temp = parent->split_a;
parent->split_a = parent->split_b;
parent->split_b = temp;
}
diff --git a/session.c b/session.c
index 7ba41bd..58c842d 100644
--- a/session.c
+++ b/session.c
@@ -1,7 +1,7 @@
#include "ted-internal.h"
#define SESSION_FILENAME "session.txt"
-#define SESSION_VERSION "\x7fTED0002"
+#define SESSION_VERSION "\x7fTED0003"
static void write_u8(FILE *fp, u8 x) {
putc(x, fp);
@@ -156,39 +156,35 @@ static BufferPos buffer_pos_read(TextBuffer *buffer, FILE *fp) {
}
-static void session_write_node(Ted *ted, FILE *fp, u16 node_idx) {
- Node *node = &ted->nodes[node_idx];
- write_u16(fp, node_idx);
+static void session_write_node(Ted *ted, FILE *fp, Node *node) {
bool is_split = !node->tabs;
write_bool(fp, is_split);
if (is_split) {
write_float(fp, node->split_pos);
write_bool(fp, node->split_vertical);
- write_u16(fp, node->split_a);
- write_u16(fp, node->split_b);
+ write_u16(fp, (u16)arr_index_of(ted->nodes, node->split_a));
+ write_u16(fp, (u16)arr_index_of(ted->nodes, node->split_b));
} else {
write_u16(fp, node->active_tab); // active tab
write_u16(fp, (u16)arr_len(node->tabs)); // ntabs
- arr_foreach_ptr(node->tabs, u16, tab) {
- write_u16(fp, *tab);
+ arr_foreach_ptr(node->tabs, TextBufferPtr, pbuf) {
+ write_u16(fp, (u16)arr_index_of(ted->buffers, *pbuf));
}
}
}
-static void session_read_node(Ted *ted, FILE *fp) {
- u16 node_idx = read_u16(fp);
- if (node_idx >= TED_MAX_NODES) {
- debug_println("WARNING: Invalid node index (see %s:%d)!\n", __FILE__, __LINE__);
- return;
- }
- ted->nodes_used[node_idx] = true;
- Node *node = &ted->nodes[node_idx];
+static Status session_read_node(Ted *ted, FILE *fp) {
+ Node *node = ted_new_node(ted);
bool is_split = read_bool(fp);
if (is_split) {
node->split_pos = clampf(read_float(fp), 0, 1);
node->split_vertical = read_bool(fp);
- node->split_a = clamp_u16(read_u16(fp), 0, TED_MAX_NODES);
- node->split_b = clamp_u16(read_u16(fp), 0, TED_MAX_NODES);
+ u16 split_a_index = read_u16(fp), split_b_index = read_u16(fp);
+ if (split_a_index == split_b_index || split_a_index >= arr_len(ted->nodes) || split_b_index >= arr_len(ted->nodes)) {
+ return false;
+ }
+ node->split_a = ted->nodes[split_a_index];
+ node->split_b = ted->nodes[split_b_index];
} else {
node->active_tab = read_u16(fp);
u16 ntabs = clamp_u16(read_u16(fp), 0, TED_MAX_TABS);
@@ -196,15 +192,14 @@ static void session_read_node(Ted *ted, FILE *fp) {
node->active_tab = 0;
for (u16 i = 0; i < ntabs; ++i) {
u16 buf_idx = read_u16(fp);
- if (buf_idx >= TED_MAX_BUFFERS) continue;
- arr_add(node->tabs, buf_idx);
+ if (buf_idx >= arr_len(ted->buffers)) return false;
+ arr_add(node->tabs, ted->buffers[buf_idx]);
}
}
+ return true;
}
-static void session_write_buffer(Ted *ted, FILE *fp, u16 buffer_idx) {
- write_u16(fp, buffer_idx);
- TextBuffer *buffer = &ted->buffers[buffer_idx];
+static void session_write_buffer(FILE *fp, TextBuffer *buffer) {
// some info about the buffer that should be restored
if (buffer_is_named_file(buffer))
write_cstr(fp, buffer->path);
@@ -219,14 +214,8 @@ static void session_write_buffer(Ted *ted, FILE *fp, u16 buffer_idx) {
buffer_pos_write(buffer->selection_pos, fp);
}
-static void session_read_buffer(Ted *ted, FILE *fp) {
- u16 buffer_idx = read_u16(fp);
- if (buffer_idx >= TED_MAX_BUFFERS) {
- debug_println("WARNING: Invalid buffer index (see %s:%d)!\n", __FILE__, __LINE__);
- return;
- }
- TextBuffer *buffer = &ted->buffers[buffer_idx];
- ted->buffers_used[buffer_idx] = true;
+static bool session_read_buffer(Ted *ted, FILE *fp) {
+ TextBuffer *buffer = ted_new_buffer(ted);
char filename[TED_PATH_MAX] = {0};
read_cstr(fp, filename, sizeof filename);
buffer_create(buffer, ted);
@@ -251,6 +240,7 @@ static void session_read_buffer(Ted *ted, FILE *fp) {
buffer->selection = false;
}
}
+ return true;
}
static void session_write_file(Ted *ted, FILE *fp) {
@@ -258,24 +248,19 @@ static void session_write_file(Ted *ted, FILE *fp) {
write_cstr(fp, ted->cwd);
- write_u16(fp, ted->active_node ? (u16)(ted->active_node - ted->nodes) : U16_MAX); // active node idx
- write_u16(fp, ted->active_buffer ? (u16)(ted->active_buffer - ted->buffers) : U16_MAX); // active buffer idx
+ write_u16(fp, (u16)arr_index_of(ted->nodes, ted->active_node)); // active node idx
+ write_u16(fp, (u16)arr_index_of(ted->buffers, ted->active_buffer)); // active buffer idx
- u16 nnodes = 0;
- for (u16 i = 0; i < TED_MAX_NODES; ++i)
- nnodes += ted->nodes_used[i];
- write_u16(fp, nnodes);
- for (u16 i = 0; i < TED_MAX_NODES; ++i)
- if (ted->nodes_used[i])
- session_write_node(ted, fp, i);
-
- u16 nbuffers = 0;
- for (u16 i = 0; i < TED_MAX_BUFFERS; ++i)
- nbuffers += ted->buffers_used[i];
- write_u16(fp, nbuffers);
- for (u16 i = 0; i < TED_MAX_BUFFERS; ++i)
- if (ted->buffers_used[i])
- session_write_buffer(ted, fp, i);
+ write_u16(fp, (u16)arr_len(ted->buffers));
+ arr_foreach_ptr(ted->buffers, TextBufferPtr, pbuffer) {
+ TextBuffer *buffer = *pbuffer;
+ session_write_buffer(fp, buffer);
+ }
+
+ write_u16(fp, (u16)arr_len(ted->nodes));
+ arr_foreach_ptr(ted->nodes, NodePtr, pnode) {
+ session_write_node(ted, fp, *pnode);
+ }
}
static void session_read_file(Ted *ted, FILE *fp) {
@@ -291,40 +276,51 @@ static void session_read_file(Ted *ted, FILE *fp) {
u16 active_node_idx = read_u16(fp);
u16 active_buffer_idx = read_u16(fp);
- u16 nnodes = clamp_u16(read_u16(fp), 0, TED_MAX_NODES);
- for (u16 i = 0; i < nnodes; ++i) {
- session_read_node(ted, fp);
- }
-
- u16 nbuffers = clamp_u16(read_u16(fp), 0, TED_MAX_BUFFERS);
+ u16 nbuffers = read_u16(fp);
for (u16 i = 0; i < nbuffers; ++i) {
- session_read_buffer(ted, fp);
+ if (!session_read_buffer(ted, fp)) {
+ arr_foreach_ptr(ted->buffers, TextBufferPtr, pbuffer) {
+ buffer_free(*pbuffer);
+ }
+ arr_clear(ted->buffers);
+ return;
+ }
+ }
+
+ u16 nnodes = read_u16(fp);
+ for (u16 i = 0; i < nnodes; ++i) {
+ if (!session_read_node(ted, fp)) {
+ arr_foreach_ptr(ted->buffers, TextBufferPtr, pbuffer) {
+ buffer_free(*pbuffer);
+ }
+ arr_clear(ted->buffers);
+ arr_foreach_ptr(ted->nodes, NodePtr, pnode) {
+ node_free(*pnode);
+ }
+ arr_clear(ted->nodes);
+ return;
+ }
}
if (active_node_idx == U16_MAX) {
ted->active_node = NULL;
} else {
- active_node_idx = clamp_u16(active_node_idx, 0, TED_MAX_NODES);
- if (ted->nodes_used[active_node_idx])
- ted->active_node = &ted->nodes[active_node_idx];
+ if (active_node_idx >= arr_len(ted->nodes))
+ active_node_idx = 0;
+ ted->active_node = ted->nodes[active_node_idx];
}
if (active_buffer_idx == U16_MAX) {
ted->active_buffer = NULL;
} else {
- active_buffer_idx = clamp_u16(active_buffer_idx, 0, TED_MAX_BUFFERS);
- if (ted->buffers_used[active_buffer_idx])
- ted->active_buffer = &ted->buffers[active_buffer_idx];
+ if (active_buffer_idx >= arr_len(ted->buffers))
+ active_buffer_idx = 0;
+ ted->active_buffer = ted->buffers[active_buffer_idx];
}
if (nbuffers && !ted->active_buffer) {
// set active buffer to something
- for (u16 i = 0; i < TED_MAX_BUFFERS; ++i) {
- if (ted->buffers_used[i]) {
- ted_switch_to_buffer(ted, &ted->buffers[i]);
- break;
- }
- }
+ ted->active_buffer = ted->buffers[0];
}
}
diff --git a/ted-internal.h b/ted-internal.h
index 36d15d2..38a7dbc 100644
--- a/ted-internal.h
+++ b/ted-internal.h
@@ -301,24 +301,20 @@ struct FileSelector {
// A node is a collection of tabs OR a split of two node
struct Node {
- /// dynamic array of indices into ted->buffers, or `NULL` if this is a split
- u16 *tabs;
+ /// dynamic array of buffers, or `NULL` if this is a split
+ TextBuffer **tabs;
/// number from 0 to 1 indicating where the split is.
float split_pos;
/// index of active tab in `tabs`.
u16 active_tab;
/// is the split vertical? if false, this split looks like a|b
bool split_vertical;
- /// split left/upper half; index into `ted->nodes`
- u16 split_a;
+ /// split left/upper half
+ Node *split_a;
/// split right/lower half
- u16 split_b;
+ Node *split_b;
};
-/// max number of buffers open at one time
-#define TED_MAX_BUFFERS 256
-/// max number of nodes open at one time
-#define TED_MAX_NODES 256
/// max tabs per node
#define TED_MAX_TABS 100
/// max strings in all config files
@@ -421,6 +417,9 @@ typedef struct {
vec2 pos;
} MouseRelease;
+typedef TextBuffer *TextBufferPtr;
+typedef Node *NodePtr;
+
struct Ted {
/// all running LSP servers
LSP *lsps[TED_LSP_MAX + 1];
@@ -570,12 +569,9 @@ struct Ted {
char build_dir[TED_PATH_MAX];
/// where we are reading tags from
char tags_dir[TED_PATH_MAX];
- bool nodes_used[TED_MAX_NODES];
/// `nodes[0]` is always the "root node", if any buffers are open.
- Node nodes[TED_MAX_NODES];
- /// NOTE: the buffer at index 0 is reserved as a "null buffer" and should not be used.
- bool buffers_used[TED_MAX_BUFFERS];
- TextBuffer buffers[TED_MAX_BUFFERS];
+ Node **nodes;
+ TextBuffer **buffers;
/// number of config file strings
u32 nstrings;
/// config file strings
@@ -844,8 +840,12 @@ void ted_go_to_lsp_document_position(Ted *ted, LSP *lsp, LSPDocumentPosition pos
void ted_cancel_lsp_request(Ted *ted, LSPServerRequestID *request);
/// convert LSPWindowMessageType to MessageType
MessageType ted_message_type_from_lsp(LSPWindowMessageType type);
-/// Returns the index of an available node, or -1 if none are available
-i32 ted_new_node(Ted *ted);
+/// delete buffer - does NOT remove it from the node tree
+void ted_delete_buffer(Ted *ted, TextBuffer *buffer);
+/// Returns a new node, or NULL on out of memory
+Node *ted_new_node(Ted *ted);
+/// Returns a new buffer, or NULL on out of memory
+TextBuffer *ted_new_buffer(Ted *ted);
/// check for orphaned nodes and node cycles
void ted_check_for_node_problems(Ted *ted);
/// load ted configuration
diff --git a/ted.c b/ted.c
index 211a07b..aef59fd 100644
--- a/ted.c
+++ b/ted.c
@@ -295,7 +295,7 @@ void ted_path_full(Ted *ted, const char *relpath, char *abspath, size_t abspath_
}
static bool ted_is_regular_buffer(Ted *ted, TextBuffer *buffer) {
- return buffer >= ted->buffers && buffer < ted->buffers + TED_MAX_BUFFERS;
+ return arr_index_of(ted->buffers, buffer) >= 0;
}
Status ted_get_file(Ted const *ted, const char *name, char *out, size_t outsz) {
@@ -430,21 +430,15 @@ void ted_free_fonts(Ted *ted) {
// get node and tab containing buffer
static Node *ted_buffer_location_in_node_tree(Ted *ted, TextBuffer *buffer, u16 *tab_idx) {
- // now we need to figure out where this buffer is
- u16 idx = (u16)(buffer - ted->buffers);
- const bool *nodes_used = ted->nodes_used;
- for (u16 i = 0; i < TED_MAX_NODES; ++i) {
- if (!nodes_used[i]) continue;
- Node *node = &ted->nodes[i];
- arr_foreach_ptr(node->tabs, u16, tab) {
- if (idx == *tab) {
- if (tab_idx)
- *tab_idx = (u16)(tab - node->tabs);
- return node;
- }
+ arr_foreach_ptr(ted->nodes, NodePtr, pnode) {
+ Node *node = *pnode;
+ i32 index = arr_index_of(node->tabs, buffer);
+ if (index >= 0) {
+ if (tab_idx)
+ *tab_idx = (u16)index;
+ return node;
}
}
- assert(0);
return NULL;
}
@@ -457,10 +451,14 @@ void ted_switch_to_buffer(Ted *ted, TextBuffer *buffer) {
find_update(ted, true); // make sure find results are for this file
}
- if (buffer >= ted->buffers && buffer < ted->buffers + TED_MAX_BUFFERS) {
+ if (ted_is_regular_buffer(ted, buffer)) {
ted->prev_active_buffer = buffer;
u16 active_tab=0;
Node *node = ted_buffer_location_in_node_tree(ted, buffer, &active_tab);
+ if (!node) {
+ assert(0);
+ return;
+ }
node->active_tab = active_tab;
ted->active_node = node;
signature_help_retrigger(ted);
@@ -471,11 +469,11 @@ void ted_switch_to_buffer(Ted *ted, TextBuffer *buffer) {
}
void ted_reset_active_buffer(Ted *ted) {
- if (ted->nodes_used[0]) {
- Node *node = &ted->nodes[0];
+ if (arr_len(ted->nodes)) {
+ Node *node = ted->nodes[0];
while (!node->tabs)
- node = &ted->nodes[node->split_a]; // arbitrarily pick split_a.
- ted_switch_to_buffer(ted, &ted->buffers[node->tabs[node->active_tab]]);
+ node = node->split_a; // arbitrarily pick split_a.
+ ted_switch_to_buffer(ted, node->tabs[node->active_tab]);
} else {
// there's nothing to set it to
ted_switch_to_buffer(ted, NULL);
@@ -483,41 +481,36 @@ void ted_reset_active_buffer(Ted *ted) {
}
-static i32 ted_new_buffer(Ted *ted) {
- bool *buffers_used = ted->buffers_used;
- for (i32 i = 1; // start from 1, so as not to use the null buffer
- i < TED_MAX_BUFFERS; ++i) {
- if (!buffers_used[i]) {
- buffers_used[i] = true;
- buffer_create(&ted->buffers[i], ted);
- return i;
- }
- }
- return -1;
-}
-
-void ted_delete_buffer(Ted *ted, u16 index) {
- TextBuffer *buffer = &ted->buffers[index];
+void ted_delete_buffer(Ted *ted, TextBuffer *buffer) {
if (buffer == ted->active_buffer)
ted_switch_to_buffer(ted, NULL); // make sure we don't set the active buffer to something invalid
if (buffer == ted->prev_active_buffer)
ted->prev_active_buffer = NULL;
buffer_free(buffer);
- ted->buffers_used[index] = false;
+ arr_remove_item(ted->buffers, buffer);
}
-i32 ted_new_node(Ted *ted) {
- bool *nodes_used = ted->nodes_used;
- for (i32 i = 0; i < TED_MAX_NODES; ++i) {
- if (!nodes_used[i]) {
- memset(&ted->nodes[i], 0, sizeof ted->nodes[i]); // zero new node
- nodes_used[i] = true;
- return i;
- }
+Node *ted_new_node(Ted *ted) {
+ if (arr_len(ted->nodes) >= 100) { // TODO: constant
+ ted_error(ted, "Too many nodes.");
+ return NULL;
}
- ted_error(ted, "Too many nodes.");
- return -1;
-
+ Node *node = ted_calloc(ted, 1, sizeof *node);
+ if (!node) return NULL;
+ arr_add(ted->nodes, node);
+ return node;
+}
+
+TextBuffer *ted_new_buffer(Ted *ted) {
+ if (arr_len(ted->buffers) >= 100) { // TODO: constant
+ ted_error(ted, "Too many buffers.");
+ return NULL;
+ }
+ TextBuffer *buffer = ted_calloc(ted, 1, sizeof *buffer);
+ if (!buffer) return NULL;
+ buffer_create(buffer, ted);
+ arr_add(ted->buffers, buffer);
+ return buffer;
}
float ted_line_buffer_height(Ted *ted) {
@@ -527,50 +520,42 @@ float ted_line_buffer_height(Ted *ted) {
void ted_node_switch(Ted *ted, Node *node) {
assert(node->tabs);
- ted_switch_to_buffer(ted, &ted->buffers[node->tabs[node->active_tab]]);
-}
-
-// Open a new buffer. Fills out *tab to the index of the tab used, and *buffer_idx to the index of the buffer.
-// Returns true on success.
-static Status ted_open_buffer(Ted *ted, u16 *buffer_idx, u16 *tab) {
- i32 new_buffer_index = ted_new_buffer(ted);
- if (new_buffer_index >= 0) {
- Node *node = ted->active_node;
- if (!node) {
- if (!ted->nodes_used[0]) {
- // no nodes open; create a root node
- i32 node_idx = ted_new_node(ted);
- assert(node_idx == 0);
- node = &ted->nodes[node_idx];
- ted->active_node = node;
- } else if (ted->prev_active_buffer) {
- // opening a file while a menu is open
- // it may happen.... (currently happens for rename symbol)
- node = ted_buffer_location_in_node_tree(ted, ted->prev_active_buffer, NULL);
- } else {
- // idk what is going on
- ted_error(ted, "internal error: can't figure out where to put this buffer.");
- ted_delete_buffer(ted, (u16)new_buffer_index);
- return false;
- }
- }
- if (arr_len(node->tabs) < TED_MAX_TABS) {
- arr_add(node->tabs, (u16)new_buffer_index);
- TextBuffer *new_buffer = &ted->buffers[new_buffer_index];
- buffer_create(new_buffer, ted);
- node->active_tab = (u16)(arr_len(node->tabs) - 1);
- *buffer_idx = (u16)new_buffer_index;
- *tab = node->active_tab;
- ted_switch_to_buffer(ted, new_buffer);
- return true;
+ ted_switch_to_buffer(ted, node->tabs[node->active_tab]);
+ ted->active_node = node;
+}
+
+// Open a new buffer. Fills out *tab to the index of the tab used.
+static TextBuffer *ted_open_buffer(Ted *ted, u16 *tab) {
+ TextBuffer *new_buffer = ted_new_buffer(ted);
+ if (!new_buffer) return NULL;
+ Node *node = ted->active_node;
+ if (!node) {
+ if (!arr_len(ted->nodes)) {
+ // no nodes open; create a root node
+ node = ted->active_node = ted_new_node(ted);
+ } else if (ted->prev_active_buffer) {
+ // opening a file while a menu is open
+ // it may happen.... (currently happens for rename symbol)
+ node = ted_buffer_location_in_node_tree(ted, ted->prev_active_buffer, NULL);
} else {
- ted_error(ted, "Too many tabs.");
- ted_delete_buffer(ted, (u16)new_buffer_index);
- return false;
+ // idk what is going on
+ ted_error(ted, "internal error: can't figure out where to put this buffer.");
+ ted_delete_buffer(ted, new_buffer);
+ return NULL;
}
- } else {
+ }
+
+ if (arr_len(node->tabs) >= TED_MAX_TABS) {
+ ted_error(ted, "Too many tabs.");
+ ted_delete_buffer(ted, new_buffer);
return false;
}
+
+ arr_add(node->tabs, new_buffer);
+ node->active_tab = (u16)(arr_len(node->tabs) - 1);
+ *tab = node->active_tab;
+ ted_switch_to_buffer(ted, new_buffer);
+ return new_buffer;
}
TextBuffer *ted_get_buffer_with_file(Ted *ted, const char *path) {
@@ -580,13 +565,10 @@ TextBuffer *ted_get_buffer_with_file(Ted *ted, const char *path) {
return NULL;
}
- bool *buffers_used = ted->buffers_used;
- TextBuffer *buffers = ted->buffers;
- for (u16 i = 0; i < TED_MAX_BUFFERS; ++i) {
- if (buffers_used[i]) {
- if (buffers[i].path && paths_eq(path, buffers[i].path)) {
- return &buffers[i];
- }
+ arr_foreach_ptr(ted->buffers, TextBufferPtr, pbuffer) {
+ TextBuffer *buffer = *pbuffer;
+ if (buffer->path && paths_eq(path, buffer->path)) {
+ return buffer;
}
}
return NULL;
@@ -609,12 +591,14 @@ bool ted_open_file(Ted *ted, const char *filename) {
}
// not open; we need to load it
- u16 buffer_idx, tab_idx;
- if (ted->active_buffer && !ted->active_buffer->path && buffer_empty(ted->active_buffer)) {
+ u16 tab_idx;
+ TextBuffer *buffer = NULL;
+ if (ted->active_buffer && !ted->active_buffer->path
+ && ted_is_regular_buffer(ted, ted->active_buffer)
+ && buffer_empty(ted->active_buffer)) {
// the active buffer is just an empty untitled buffer. open it here.
return buffer_load_file(ted->active_buffer, path);
- } else if (ted_open_buffer(ted, &buffer_idx, &tab_idx)) {
- TextBuffer *buffer = &ted->buffers[buffer_idx];
+ } else if ((buffer = ted_open_buffer(ted, &tab_idx))) {
if (buffer_load_file(buffer, path)) {
return true;
} else {
@@ -628,7 +612,7 @@ bool ted_open_file(Ted *ted, const char *filename) {
}
bool ted_new_file(Ted *ted, const char *filename) {
- u16 buffer_idx, tab_idx;
+ u16 tab_idx=0;
char path[TED_PATH_MAX];
if (filename)
ted_path_full(ted, filename, path, sizeof path);
@@ -638,8 +622,7 @@ bool ted_new_file(Ted *ted, const char *filename) {
if (buffer) {
ted_switch_to_buffer(ted, buffer);
return true;
- } else if (ted_open_buffer(ted, &buffer_idx, &tab_idx)) {
- buffer = &ted->buffers[buffer_idx];
+ } else if ((buffer = ted_open_buffer(ted, &tab_idx))) {
buffer_new_file(buffer, *path ? path : NULL);
if (!buffer_has_error(buffer)) {
return true;
@@ -656,21 +639,18 @@ bool ted_new_file(Ted *ted, const char *filename) {
bool ted_save_all(Ted *ted) {
bool success = true;
- bool *buffers_used = ted->buffers_used;
- for (u16 i = 0; i < TED_MAX_BUFFERS; ++i) {
- if (buffers_used[i]) {
- TextBuffer *buffer = &ted->buffers[i];
- if (buffer_unsaved_changes(buffer)) {
- if (!buffer->path) {
- ted_switch_to_buffer(ted, buffer);
- menu_open(ted, MENU_SAVE_AS);
- success = false; // we haven't saved this buffer yet; we've just opened the "save as" menu.
- break;
- } else {
- if (!buffer_save(buffer)) {
- success = false;
- ted_error_from_buffer(ted, buffer);
- }
+ arr_foreach_ptr(ted->buffers, TextBufferPtr, pbuffer) {
+ TextBuffer *buffer = *pbuffer;
+ if (buffer_unsaved_changes(buffer)) {
+ if (!buffer->path) {
+ ted_switch_to_buffer(ted, buffer);
+ menu_open(ted, MENU_SAVE_AS);
+ success = false; // we haven't saved this buffer yet; we've just opened the "save as" menu.
+ break;
+ } else {
+ if (!buffer_save(buffer)) {
+ success = false;
+ ted_error_from_buffer(ted, buffer);
}
}
}
@@ -679,13 +659,10 @@ bool ted_save_all(Ted *ted) {
}
void ted_reload_all(Ted *ted) {
- bool *buffers_used = ted->buffers_used;
- for (u64 i = 0; i < TED_MAX_BUFFERS; ++i) {
- if (buffers_used[i]) {
- TextBuffer *buffer = &ted->buffers[i];
- if (!buffer_unsaved_changes(buffer)) {
- buffer_reload(buffer);
- }
+ arr_foreach_ptr(ted->buffers, TextBufferPtr, pbuffer) {
+ TextBuffer *buffer = *pbuffer;
+ if (!buffer_unsaved_changes(buffer)) {
+ buffer_reload(buffer);
}
}
if (menu_is_open(ted, MENU_ASK_RELOAD)) {
@@ -763,18 +740,13 @@ void ted_press_key(Ted *ted, SDL_Keycode keycode, SDL_Keymod modifier) {
}
bool ted_get_mouse_buffer_pos(Ted *ted, TextBuffer **pbuffer, BufferPos *ppos) {
- for (u32 i = 0; i < TED_MAX_NODES; ++i) {
- if (ted->nodes_used[i]) {
- Node *node = &ted->nodes[i];
- if (node->tabs) {
- TextBuffer *buffer = &ted->buffers[node->tabs[node->active_tab]];
- BufferPos pos = {0};
- if (buffer_pixels_to_pos(buffer, ted->mouse_pos, &pos)) {
- if (ppos) *ppos = pos;
- if (pbuffer) *pbuffer = buffer;
- return true;
- }
- }
+ arr_foreach_ptr(ted->buffers, TextBufferPtr, pbuf) {
+ TextBuffer *buffer = *pbuf;
+ BufferPos pos = {0};
+ if (buffer_pixels_to_pos(buffer, ted->mouse_pos, &pos)) {
+ if (ppos) *ppos = pos;
+ if (pbuffer) *pbuffer = buffer;
+ return true;
}
}
return false;
@@ -806,32 +778,35 @@ void ted_cancel_lsp_request(Ted *ted, LSPServerRequestID *request) {
}
-static void mark_node_reachable(Ted *ted, u16 node, bool reachable[TED_MAX_NODES]) {
- if (reachable[node]) {
- ted_error(ted, "Node %u reachable in 2 different ways\nThis should never happen.", node);
- ted_log(ted, "Node %u reachable in 2 different ways\n", node);
+static void mark_node_reachable(Ted *ted, Node *node, bool *reachable) {
+ i32 i = arr_index_of(ted->nodes, node);
+ if (i < 0) return;
+ if (reachable[i]) {
+ ted_error(ted, "Node %d reachable in 2 different ways\nThis should never happen.", i);
+ ted_log(ted, "Node %d reachable in 2 different ways\n", i);
node_close(ted, node);
return;
}
- reachable[node] = true;
- Node *n = &ted->nodes[node];
- if (!n->tabs) {
- mark_node_reachable(ted, n->split_a, reachable);
- mark_node_reachable(ted, n->split_b, reachable);
+ reachable[i] = true;
+ if (!node->tabs) {
+ mark_node_reachable(ted, node->split_a, reachable);
+ mark_node_reachable(ted, node->split_b, reachable);
}
}
void ted_check_for_node_problems(Ted *ted) {
- bool reachable[TED_MAX_NODES] = {0};
- if (ted->nodes_used[0])
- mark_node_reachable(ted, 0, reachable);
- for (u16 i = 0; i < TED_MAX_NODES; ++i) {
- if (ted->nodes_used[i] && !reachable[i]) {
+ bool *reachable = ted_calloc(ted, arr_len(ted->nodes), 1);
+ if (arr_len(ted->nodes))
+ mark_node_reachable(ted, ted->nodes[0], reachable);
+ for (u32 i = 0; i < arr_len(ted->nodes); ++i) {
+ if (!reachable[i]) {
ted_error(ted, "ORPHANED NODE %u\nThis should never happen.", i);
ted_log(ted, "ORPHANED NODE %u\n", i);
- node_close(ted, i);
+ node_close(ted, ted->nodes[i]);
+ --i;
}
}
+ free(reachable);
}
MessageType ted_message_type_from_lsp(LSPWindowMessageType type) {
diff --git a/ted.h b/ted.h
index 0ead024..b1108bc 100644
--- a/ted.h
+++ b/ted.h
@@ -902,14 +902,28 @@ void node_tab_switch(Ted *ted, Node *node, i32 tab);
///
/// if `node` is a split or either index is out of range, nothing happens.
void node_tabs_swap(Node *node, i32 tab1, i32 tab2);
-/// returns index of parent in ted->nodes, or -1 if this is the root node.
-i32 node_parent(Ted *ted, i32 node_idx);
+/* TODO
+/// get two children of split node.
+///
+/// if `node` isn't a split, returns false and sets `*child1` and `*child2` to `NULL`.
+bool node_children(Node *node, Node **child1, Node **child2);
+/// returns true if this node is a vertical split
+bool node_split_is_vertical(Node *node);
+/// get number of tabs in node
+u32 node_tab_count(Ted *ted, Node *node);
+/// get buffer in tab at index of node.
+///
+/// returns `NULL` if `tab` is out of range.
+TextBuffer *node_tab_get(Ted *ted, Node *node, u32 tab);
+*/
+/// returns parent node, or NULL if this is the root node.
+Node *node_parent(Ted *ted, Node *node);
/// join this node with its sibling
void node_join(Ted *ted, Node *node);
/// close a node, WITHOUT checking for unsaved changes
///
-/// does nothing if node_idx is out of range
-void node_close(Ted *ted, i32 node_idx);
+/// does nothing if `node` is `NULL`.
+void node_close(Ted *ted, Node *node);
/// close tab, WITHOUT checking for unsaved changes!
/// returns true if the node is still open
///
@@ -1069,9 +1083,6 @@ bool ted_open_file(Ted *ted, const char *filename);
/// if `filename` is NULL, this creates an untitled buffer.
/// returns true on success.
bool ted_new_file(Ted *ted, const char *filename);
-/// Opposite of ted_new_buffer
-/// Make sure you set active_buffer to something else if you delete it!
-void ted_delete_buffer(Ted *ted, u16 index);
/// save all changes to all buffers with unsaved changes.
bool ted_save_all(Ted *ted);
/// sets the active buffer to this buffer, and updates active_node, etc. accordingly
diff --git a/unicode.h b/unicode.h
index 4e8c020..8765164 100644
--- a/unicode.h
+++ b/unicode.h
@@ -211,4 +211,15 @@ static size_t unicode_utf16_to_utf8_offset(const char *str, size_t utf16_offset)
return SIZE_MAX;
}
+static bool unicode_is_valid_utf8(const char *cstr) {
+ char32_t c = 0;
+ while (*cstr) {
+ size_t n = unicode_utf8_to_utf32(&c, cstr, 4);
+ if (n >= (size_t)-2)
+ return false;
+ cstr += n;
+ }
+ return true;
+}
+
#endif // UNICODE_H_