summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2023-08-13 22:55:06 -0300
committerpommicket <pommicket@gmail.com>2023-08-13 22:55:06 -0300
commit7f60a98f9d4f8d8a34c4de03ae9277d7872033c7 (patch)
treeba459fb75c7e89930e9bfb4850547e6d81bfe726
parent79af91046d6c9f8ddca52dee1cb72181168c0f73 (diff)
internalize Node
-rw-r--r--command.c21
-rw-r--r--main.c7
-rw-r--r--node.c109
-rw-r--r--session.c44
-rw-r--r--ted-internal.h25
-rw-r--r--ted.c46
-rw-r--r--ted.h41
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.