From 7f60a98f9d4f8d8a34c4de03ae9277d7872033c7 Mon Sep 17 00:00:00 2001 From: pommicket Date: Sun, 13 Aug 2023 22:55:06 -0300 Subject: internalize Node --- command.c | 21 ++++++----- main.c | 7 ++-- node.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- session.c | 44 +++++++++++++---------- ted-internal.h | 25 ++++--------- ted.c | 46 +++++++++--------------- ted.h | 41 +++++++++++++++------- 7 files changed, 192 insertions(+), 101 deletions(-) diff --git a/command.c b/command.c index 50696be..c017add 100644 --- a/command.c +++ b/command.c @@ -565,8 +565,12 @@ void command_execute_ex(Ted *ted, Command c, const CommandArgument *full_argumen } else if (ted->find) { find_close(ted); } else if (node) { - u16 tab_idx = node->active_tab; - buffer = node->tabs[tab_idx]; + u32 tab_idx = node_active_tab(node); + buffer = node_get_tab(node, tab_idx); + if (!buffer) { + assert(0); // active_node shouldn't be set to a split node + return; + } // (an argument of 2 overrides the unsaved changes dialog) if (argument != 2 && buffer_unsaved_changes(buffer)) { // there are unsaved changes! @@ -574,7 +578,7 @@ void command_execute_ex(Ted *ted, Command c, const CommandArgument *full_argumen strbuf_printf(ted->warn_unsaved_names, "%s", buffer_display_filename(buffer)); menu_open(ted, MENU_WARN_UNSAVED); } else { - node_tab_close(ted, node, node->active_tab); + node_tab_close(ted, node, tab_idx); } } else if (ted->build_shown) { build_stop(ted); @@ -596,18 +600,17 @@ void command_execute_ex(Ted *ted, Command c, const CommandArgument *full_argumen node_tab_prev(ted, node, (i32)argument); break; case CMD_TAB_SWITCH: - if (node && argument > I32_MIN && argument < I32_MAX) - node_tab_switch(ted, node, (i32)argument); + if (node && argument >= 0 && argument < U32_MAX) + node_tab_switch(ted, node, (u32)argument); break; case CMD_TAB_MOVE_LEFT: { - u16 active_tab = node->active_tab; + u32 active_tab = node_active_tab(node); if (active_tab > 0) node_tabs_swap(node, active_tab, active_tab - 1); } break; case CMD_TAB_MOVE_RIGHT: { - u16 active_tab = node->active_tab; - if ((uint)active_tab + 1 < arr_len(node->tabs)) - node_tabs_swap(node, active_tab, active_tab + 1); + u32 active_tab = node_active_tab(node); + node_tabs_swap(node, active_tab, active_tab + 1); } break; case CMD_FIND: if (buffer) diff --git a/main.c b/main.c index f4a69dd..230d987 100644 --- a/main.c +++ b/main.c @@ -1,6 +1,5 @@ /* TODO: -- public Node API - public Selector/FileSelector API - public Settings API @@ -688,9 +687,9 @@ int main(int argc, char **argv) { if (!menu_is_any_open(ted)) { 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)) { + TextBuffer *tab = node_get_tab(node, node_active_tab(node)); + if (tab) { + if (buffer_handle_click(ted, tab, pos, times)) { add = false; break; } diff --git a/node.c b/node.c index 4382300..9b7fbed 100644 --- a/node.c +++ b/node.c @@ -2,9 +2,102 @@ #include "ted-internal.h" +struct Node { + /// 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 + Node *split_a; + /// split right/lower half + Node *split_b; +}; + +Node *node_new(Ted *ted) { + if (arr_len(ted->nodes) >= TED_NODE_MAX) { + ted_error(ted, "Too many nodes."); + return NULL; + } + Node *node = ted_calloc(ted, 1, sizeof *node); + if (!node) return NULL; + arr_add(ted->nodes, node); + return node; +} + +void node_init_split(Node *node, Node *child1, Node *child2, float split_pos, bool is_vertical) { + assert(!node->tabs && !node->split_a); // node should not be already initialized. + assert(child1 && child2); + assert(child1 != child2); + assert(node != child1); + assert(node != child2); + + node->split_a = child1; + node->split_b = child2; + node->split_pos = split_pos; + node->split_vertical = is_vertical; +} + +Node *node_child1(Node *node) { + return node->split_a; +} + +Node *node_child2(Node *node) { + return node->split_b; +} + +float node_split_pos(Node *node) { + return node->split_pos; +} + +void node_split_set_pos(Node *node, float pos) { + node->split_pos = pos; +} + +bool node_split_is_vertical(Node *node) { + return node->split_vertical; +} + +void node_split_set_vertical(Node *node, bool is_vertical) { + node->split_vertical = is_vertical; +} + +u32 node_tab_count(Node *node) { + return arr_len(node->tabs); +} + +u32 node_active_tab(Node *node) { + return node->active_tab; +} + +TextBuffer *node_get_tab(Node *node, u32 tab) { + if (tab >= arr_len(node->tabs)) + return NULL; + return node->tabs[tab]; +} + +i32 node_index_of_tab(Node *node, TextBuffer *buffer) { + return arr_index_of(node->tabs, buffer); +} + +bool node_add_tab(Ted *ted, Node *node, TextBuffer *buffer) { + if (arr_len(node->tabs) >= TED_MAX_TABS) { + ted_error(ted, "Too many tabs."); + return false; + } + + arr_add(node->tabs, buffer); + return true; +} + static void node_switch_to_tab(Ted *ted, Node *node, u16 new_tab_index) { if (new_tab_index >= arr_len(node->tabs)) return; + if (new_tab_index == node->active_tab) + return; node->active_tab = new_tab_index; if (node == ted->active_node) { @@ -27,15 +120,15 @@ void node_tab_prev(Ted *ted, Node *node, i32 n) { node_tab_next(ted, node, -n); } -void node_tab_switch(Ted *ted, Node *node, i32 tab) { +void node_tab_switch(Ted *ted, Node *node, u32 tab) { assert(node->tabs); - if (tab >= 0 && tab < (i32)arr_len(node->tabs)) { + if (tab >= 0 && tab < node_tab_count(node)) { node_switch_to_tab(ted, node, (u16)tab); } } -void node_tabs_swap(Node *node, i32 tab1i, i32 tab2i) { - if (tab1i < 0 || tab1i >= (i32)arr_len(node->tabs) || tab2i < 0 || tab2i >= (i32)arr_len(node->tabs)) +void node_tabs_swap(Node *node, u32 tab1i, u32 tab2i) { + if (tab1i >= arr_len(node->tabs) || tab2i >= arr_len(node->tabs)) return; u16 tab1 = (u16)tab1i, tab2 = (u16)tab2i; @@ -162,12 +255,12 @@ void node_close(Ted *ted, Node *node) { } -bool node_tab_close(Ted *ted, Node *node, i32 index) { +bool node_tab_close(Ted *ted, Node *node, u32 index) { ted->dragging_tab_node = NULL; u16 ntabs = (u16)arr_len(node->tabs); - if (index < 0 || index >= ntabs) { + if (index >= ntabs) { return false; } @@ -392,8 +485,8 @@ void node_split(Ted *ted, Node *node, bool vertical) { if (node_depth(ted, node) >= 4) return; // prevent splitting too deep if (arr_len(node->tabs) > 1) { // need at least 2 tabs to split - Node *left = ted_new_node(ted); - Node *right = ted_new_node(ted); + Node *left = node_new(ted); + Node *right = node_new(ted); if (left && right) { u16 active_tab = node->active_tab; // put active tab on the right diff --git a/session.c b/session.c index 4176361..d30f534 100644 --- a/session.c +++ b/session.c @@ -157,18 +157,21 @@ static BufferPos buffer_pos_read(TextBuffer *buffer, FILE *fp) { static void session_write_node(Ted *ted, FILE *fp, Node *node) { - bool is_split = !node->tabs; + bool is_split = node_child1(node) != NULL; write_bool(fp, is_split); if (is_split) { - write_float(fp, node->split_pos); - write_bool(fp, node->split_vertical); - write_u16(fp, (u16)arr_index_of(ted->nodes, node->split_a)); - write_u16(fp, (u16)arr_index_of(ted->nodes, node->split_b)); + write_float(fp, node_split_pos(node)); + write_bool(fp, node_split_is_vertical(node)); + Node *child1 = node_child1(node), *child2 = node_child2(node); + write_u16(fp, (u16)arr_index_of(ted->nodes, child1)); + write_u16(fp, (u16)arr_index_of(ted->nodes, child2)); } else { - write_u16(fp, node->active_tab); // active tab - write_u16(fp, (u16)arr_len(node->tabs)); // ntabs - arr_foreach_ptr(node->tabs, TextBufferPtr, pbuf) { - write_u16(fp, (u16)arr_index_of(ted->buffers, *pbuf)); + write_u16(fp, (u16)node_active_tab(node)); // active tab + u16 ntabs = (u16)node_tab_count(node); + write_u16(fp, ntabs); + for (u16 i = 0; i < ntabs; ++i) { + TextBuffer *tab = node_get_tab(node, i); + write_u16(fp, (u16)arr_index_of(ted->buffers, tab)); } } } @@ -176,27 +179,30 @@ static void session_write_node(Ted *ted, FILE *fp, Node *node) { static Status session_read_node(Ted *ted, FILE *fp, Node *node) { bool is_split = read_bool(fp); if (is_split) { - node->split_pos = clampf(read_float(fp), 0, 1); - node->split_vertical = read_bool(fp); + float split_pos = clampf(read_float(fp), 0, 1); + bool vertical = read_bool(fp); 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]; - if (node->split_a == node || node->split_b == node) + Node *child1 = ted->nodes[split_a_index]; + Node *child2 = ted->nodes[split_b_index]; + if (child1 == node || child2 == node) return false; + node_init_split(node, child1, child2, split_pos, vertical); } else { - node->active_tab = read_u16(fp); + u16 active_tab = read_u16(fp); u16 ntabs = clamp_u16(read_u16(fp), 0, TED_MAX_TABS); - if (node->active_tab >= ntabs) - node->active_tab = 0; + if (active_tab >= ntabs) + active_tab = 0; for (u16 i = 0; i < ntabs; ++i) { u16 buf_idx = read_u16(fp); if (buf_idx >= arr_len(ted->buffers)) return false; - arr_add(node->tabs, ted->buffers[buf_idx]); + if (!node_add_tab(ted, node, ted->buffers[buf_idx])) + return false; } + node_tab_switch(ted, node, active_tab); } return true; } @@ -292,7 +298,7 @@ static void session_read_file(Ted *ted, FILE *fp) { u16 nnodes = read_u16(fp); for (u16 i = 0; i < nnodes; i++) { - ted_new_node(ted); + node_new(ted); } for (u16 i = 0; i < nnodes; i++) { if (!session_read_node(ted, fp, ted->nodes[i])) { diff --git a/ted-internal.h b/ted-internal.h index 1b34fd4..d35b14c 100644 --- a/ted-internal.h +++ b/ted-internal.h @@ -222,22 +222,6 @@ struct FileSelector { bool create_menu; }; -// A node is a collection of tabs OR a split of two node -struct Node { - /// 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 - Node *split_a; - /// split right/lower half - Node *split_b; -}; - /// max tabs per node #define TED_MAX_TABS 100 /// max strings in all config files @@ -722,7 +706,14 @@ void menu_shell_down(Ted *ted); Rect selection_menu_render_bg(Ted *ted); // === node.c === +Node *node_new(Ted *ted); void node_free(Node *node); +/// don't call this if `buffer` is in any other nodes! +/// +/// \returns false if there are too many tabs +Status node_add_tab(Ted *ted, Node *node, TextBuffer *buffer); +/// cannot be called if `node` has already been initialized or contains tabs. +void node_init_split(Node *node, Node *child1, Node *child2, float split_pos, bool is_vertical); void node_frame(Ted *ted, Node *node, Rect r); // === syntax.c === @@ -751,8 +742,6 @@ void ted_cancel_lsp_request(Ted *ted, LSPServerRequestID *request); MessageType ted_message_type_from_lsp(LSPWindowMessageType type); /// 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 diff --git a/ted.c b/ted.c index 771f344..c06611c 100644 --- a/ted.c +++ b/ted.c @@ -438,7 +438,7 @@ void ted_free_fonts(Ted *ted) { static Node *ted_buffer_location_in_node_tree(Ted *ted, TextBuffer *buffer, u16 *tab_idx) { arr_foreach_ptr(ted->nodes, NodePtr, pnode) { Node *node = *pnode; - i32 index = arr_index_of(node->tabs, buffer); + i32 index = node_index_of_tab(node, buffer); if (index >= 0) { if (tab_idx) *tab_idx = (u16)index; @@ -465,9 +465,9 @@ void ted_switch_to_buffer(Ted *ted, TextBuffer *buffer) { assert(0); return; } - node->active_tab = active_tab; ted->active_node = node; signature_help_retrigger(ted); + node_tab_switch(ted, node, active_tab); } else { ted->active_node = NULL; } @@ -477,9 +477,9 @@ void ted_switch_to_buffer(Ted *ted, TextBuffer *buffer) { void ted_reset_active_buffer(Ted *ted) { if (arr_len(ted->nodes)) { Node *node = ted->nodes[0]; - while (!node->tabs) - node = node->split_a; // arbitrarily pick split_a. - ted_switch_to_buffer(ted, node->tabs[node->active_tab]); + while (node_child1(node)) + node = node_child1(node); + ted_switch_to_buffer(ted, node_get_tab(node, node_active_tab(node))); } else { // there's nothing to set it to ted_switch_to_buffer(ted, NULL); @@ -496,17 +496,6 @@ void ted_delete_buffer(Ted *ted, TextBuffer *buffer) { arr_remove_item(ted->buffers, buffer); } -Node *ted_new_node(Ted *ted) { - if (arr_len(ted->nodes) >= TED_NODE_MAX) { - ted_error(ted, "Too many nodes."); - return NULL; - } - 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) >= TED_BUFFER_MAX) { ted_error(ted, "Too many buffers."); @@ -524,11 +513,10 @@ float ted_line_buffer_height(Ted *ted) { } void ted_node_switch(Ted *ted, Node *node) { - while (!node->tabs) { - node = node->split_a; + while (node_child1(node)) { + node = node_child1(node); } - ted_switch_to_buffer(ted, node->tabs[node->active_tab]); - ted->active_node = node; + ted_switch_to_buffer(ted, node_get_tab(node, node_active_tab(node))); } // Open a new buffer. Fills out *tab to the index of the tab used. @@ -539,7 +527,7 @@ static TextBuffer *ted_open_buffer(Ted *ted, u16 *tab) { if (!node) { if (!arr_len(ted->nodes)) { // no nodes open; create a root node - node = ted->active_node = ted_new_node(ted); + node = ted->active_node = node_new(ted); } else if (ted->prev_active_buffer) { // opening a file while a menu is open // it may happen.... (currently happens for rename symbol) @@ -552,16 +540,14 @@ static TextBuffer *ted_open_buffer(Ted *ted, u16 *tab) { } } - if (arr_len(node->tabs) >= TED_MAX_TABS) { - ted_error(ted, "Too many tabs."); + if (!node_add_tab(ted, node, new_buffer)) { 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); + u16 active_tab = (u16)(node_tab_count(node) - 1); + *tab = active_tab; + node_tab_switch(ted, node, active_tab); return new_buffer; } @@ -797,9 +783,9 @@ static void mark_node_reachable(Ted *ted, Node *node, bool *reachable) { return; } reachable[i] = true; - if (!node->tabs) { - mark_node_reachable(ted, node->split_a, reachable); - mark_node_reachable(ted, node->split_b, reachable); + if (node_child1(node)) { + mark_node_reachable(ted, node_child1(node), reachable); + mark_node_reachable(ted, node_child2(node), reachable); } } diff --git a/ted.h b/ted.h index 79ed424..8e68d1b 100644 --- a/ted.h +++ b/ted.h @@ -133,6 +133,8 @@ typedef struct Selector Selector; typedef struct FileSelector FileSelector; /// a split or collection of tabs +/// +/// this handles ted's split-screen and tab features. typedef struct Node Node; /// A position in the buffer @@ -940,25 +942,37 @@ void node_tab_prev(Ted *ted, Node *node, i32 n); /// switch to a specific tab. /// /// if `tab` is out of range or `node` is a split, nothing happens. -void node_tab_switch(Ted *ted, Node *node, i32 tab); +void node_tab_switch(Ted *ted, Node *node, u32 tab); /// swap the position of two tabs /// /// if `node` is a split or either index is out of range, nothing happens. -void node_tabs_swap(Node *node, i32 tab1, i32 tab2); -/* 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); +void node_tabs_swap(Node *node, u32 tab1, u32 tab2); +/// get left/top child of split node. +/// +/// returns `NULL` if `node` isn't a split node. +Node *node_child1(Node *node); +/// get right/bottom child of split node. +/// +/// returns `NULL` if `node` isn't a split node. +Node *node_child2(Node *node); +/// returns the proportion of the split devoted to the left/top child. +float node_split_pos(Node *node); +/// set proportion of split devoted to left/top child. +void node_split_set_pos(Node *node, float pos); /// returns true if this node is a vertical split bool node_split_is_vertical(Node *node); +/// set whether this node is a vertical split +void node_split_set_vertical(Node *node, bool is_vertical); /// get number of tabs in node -u32 node_tab_count(Ted *ted, Node *node); +u32 node_tab_count(Node *node); +/// get index of active tab in node +u32 node_active_tab(Node *node); +/// returns index of tab containing `buffer`, or -1 if `node` doesn't contain `buffer` +i32 node_index_of_tab(Node *node, TextBuffer *buffer); /// 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); -*/ +TextBuffer *node_get_tab(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 @@ -968,10 +982,11 @@ void node_join(Ted *ted, Node *node); /// 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 /// -/// does nothing and returns false if node_idx is out of range -bool node_tab_close(Ted *ted, Node *node, i32 index); +/// returns `true` if the node is still open +/// +/// does nothing and returns `false` if `index` is out of range +bool node_tab_close(Ted *ted, Node *node, u32 index); /// make a split void node_split(Ted *ted, Node *node, bool vertical); /// switch to the other side of the current split. -- cgit v1.2.3