1use crate::model::buffer::Buffer;
27use crate::model::cursor::Cursors;
28use crate::model::event::{BufferId, ContainerId, LeafId, SplitDirection, SplitId};
29use crate::model::marker::MarkerList;
30use crate::view::folding::FoldManager;
31use crate::view::ui::view_pipeline::Layout;
32use crate::view::viewport::Viewport;
33use crate::{services::plugins::api::ViewTransformPayload, state::ViewMode};
34use ratatui::layout::Rect;
35use serde::{Deserialize, Serialize};
36use std::collections::HashMap;
37
38#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
40pub enum SplitNode {
41 Leaf {
43 buffer_id: BufferId,
45 split_id: LeafId,
47 },
48 Split {
50 direction: SplitDirection,
52 first: Box<Self>,
54 second: Box<Self>,
56 ratio: f32,
59 split_id: ContainerId,
61 },
62}
63
64#[derive(Debug)]
71pub struct BufferViewState {
72 pub cursors: Cursors,
74
75 pub viewport: Viewport,
77
78 pub view_mode: ViewMode,
80
81 pub compose_width: Option<u16>,
83
84 pub compose_column_guides: Option<Vec<u16>>,
86
87 pub rulers: Vec<usize>,
89
90 pub show_line_numbers: bool,
95
96 pub view_transform: Option<ViewTransformPayload>,
98
99 pub view_transform_stale: bool,
103
104 pub plugin_state: std::collections::HashMap<String, serde_json::Value>,
108
109 pub folds: FoldManager,
111}
112
113impl BufferViewState {
114 pub fn ensure_cursor_visible(&mut self, buffer: &mut Buffer, marker_list: &MarkerList) {
120 let hidden: Vec<(usize, usize)> = self
121 .folds
122 .resolved_ranges(buffer, marker_list)
123 .into_iter()
124 .map(|r| (r.start_byte, r.end_byte))
125 .collect();
126 let cursor = *self.cursors.primary();
127 self.viewport.ensure_visible(buffer, &cursor, &hidden);
128 }
129
130 pub fn new(width: u16, height: u16) -> Self {
132 Self {
133 cursors: Cursors::new(),
134 viewport: Viewport::new(width, height),
135 view_mode: ViewMode::Source,
136 compose_width: None,
137 compose_column_guides: None,
138 rulers: Vec::new(),
139 show_line_numbers: true,
140 view_transform: None,
141 view_transform_stale: false,
142 plugin_state: std::collections::HashMap::new(),
143 folds: FoldManager::new(),
144 }
145 }
146
147 pub fn apply_config_defaults(
154 &mut self,
155 line_numbers: bool,
156 line_wrap: bool,
157 wrap_indent: bool,
158 rulers: Vec<usize>,
159 ) {
160 self.show_line_numbers = line_numbers;
161 self.viewport.line_wrap_enabled = line_wrap;
162 self.viewport.wrap_indent = wrap_indent;
163 self.rulers = rulers;
164 }
165}
166
167impl Clone for BufferViewState {
168 fn clone(&self) -> Self {
169 Self {
170 cursors: self.cursors.clone(),
171 viewport: self.viewport.clone(),
172 view_mode: self.view_mode.clone(),
173 compose_width: self.compose_width,
174 compose_column_guides: self.compose_column_guides.clone(),
175 rulers: self.rulers.clone(),
176 show_line_numbers: self.show_line_numbers,
177 view_transform: self.view_transform.clone(),
178 view_transform_stale: self.view_transform_stale,
179 plugin_state: self.plugin_state.clone(),
180 folds: FoldManager::new(),
182 }
183 }
184}
185
186#[derive(Debug, Clone)]
198pub struct SplitViewState {
199 pub active_buffer: BufferId,
201
202 pub keyed_states: HashMap<BufferId, BufferViewState>,
204
205 pub open_buffers: Vec<BufferId>,
208
209 pub tab_scroll_offset: usize,
211
212 pub layout: Option<Layout>,
215
216 pub layout_dirty: bool,
218
219 pub focus_history: Vec<BufferId>,
222
223 pub sync_group: Option<u32>,
226
227 pub composite_view: Option<BufferId>,
232}
233
234impl std::ops::Deref for SplitViewState {
235 type Target = BufferViewState;
236
237 fn deref(&self) -> &BufferViewState {
238 self.active_state()
239 }
240}
241
242impl std::ops::DerefMut for SplitViewState {
243 fn deref_mut(&mut self) -> &mut BufferViewState {
244 self.active_state_mut()
245 }
246}
247
248impl SplitViewState {
249 pub fn with_buffer(width: u16, height: u16, buffer_id: BufferId) -> Self {
251 let buf_state = BufferViewState::new(width, height);
252 let mut keyed_states = HashMap::new();
253 keyed_states.insert(buffer_id, buf_state);
254 Self {
255 active_buffer: buffer_id,
256 keyed_states,
257 open_buffers: vec![buffer_id],
258 tab_scroll_offset: 0,
259 layout: None,
260 layout_dirty: true,
261 focus_history: Vec::new(),
262 sync_group: None,
263 composite_view: None,
264 }
265 }
266
267 pub fn active_state(&self) -> &BufferViewState {
269 self.keyed_states
270 .get(&self.active_buffer)
271 .expect("active_buffer must always have an entry in keyed_states")
272 }
273
274 pub fn active_state_mut(&mut self) -> &mut BufferViewState {
276 self.keyed_states
277 .get_mut(&self.active_buffer)
278 .expect("active_buffer must always have an entry in keyed_states")
279 }
280
281 pub fn switch_buffer(&mut self, new_buffer_id: BufferId) {
287 if new_buffer_id == self.active_buffer {
288 return;
289 }
290 if !self.keyed_states.contains_key(&new_buffer_id) {
292 let active = self.active_state();
293 let width = active.viewport.width;
294 let height = active.viewport.height;
295 self.keyed_states
296 .insert(new_buffer_id, BufferViewState::new(width, height));
297 }
298 self.active_buffer = new_buffer_id;
299 self.layout_dirty = true;
301 }
302
303 pub fn buffer_state(&self, buffer_id: BufferId) -> Option<&BufferViewState> {
305 self.keyed_states.get(&buffer_id)
306 }
307
308 pub fn buffer_state_mut(&mut self, buffer_id: BufferId) -> Option<&mut BufferViewState> {
310 self.keyed_states.get_mut(&buffer_id)
311 }
312
313 pub fn ensure_buffer_state(&mut self, buffer_id: BufferId) -> &mut BufferViewState {
316 let (width, height) = {
317 let active = self.active_state();
318 (active.viewport.width, active.viewport.height)
319 };
320 self.keyed_states
321 .entry(buffer_id)
322 .or_insert_with(|| BufferViewState::new(width, height))
323 }
324
325 pub fn remove_buffer_state(&mut self, buffer_id: BufferId) {
327 if buffer_id != self.active_buffer {
328 self.keyed_states.remove(&buffer_id);
329 }
330 }
331
332 pub fn invalidate_layout(&mut self) {
334 self.layout_dirty = true;
335 }
336
337 pub fn ensure_layout(
345 &mut self,
346 tokens: &[fresh_core::api::ViewTokenWire],
347 source_range: std::ops::Range<usize>,
348 tab_size: usize,
349 ) -> &Layout {
350 if self.layout.is_none() || self.layout_dirty {
351 self.layout = Some(Layout::from_tokens(tokens, source_range, tab_size));
352 self.layout_dirty = false;
353 }
354 self.layout.as_ref().unwrap()
355 }
356
357 pub fn get_layout(&self) -> Option<&Layout> {
359 if self.layout_dirty {
360 None
361 } else {
362 self.layout.as_ref()
363 }
364 }
365
366 pub fn add_buffer(&mut self, buffer_id: BufferId) {
368 if !self.open_buffers.contains(&buffer_id) {
369 self.open_buffers.push(buffer_id);
370 }
371 }
372
373 pub fn remove_buffer(&mut self, buffer_id: BufferId) {
375 self.open_buffers.retain(|&id| id != buffer_id);
376 if buffer_id != self.active_buffer {
378 self.keyed_states.remove(&buffer_id);
379 }
380 }
381
382 pub fn has_buffer(&self, buffer_id: BufferId) -> bool {
384 self.open_buffers.contains(&buffer_id)
385 }
386
387 pub fn push_focus(&mut self, buffer_id: BufferId) {
390 self.focus_history.retain(|&id| id != buffer_id);
392 self.focus_history.push(buffer_id);
393 if self.focus_history.len() > 50 {
395 self.focus_history.remove(0);
396 }
397 }
398
399 pub fn previous_buffer(&self) -> Option<BufferId> {
401 self.focus_history.last().copied()
402 }
403
404 pub fn pop_focus(&mut self) -> Option<BufferId> {
406 self.focus_history.pop()
407 }
408
409 pub fn remove_from_history(&mut self, buffer_id: BufferId) {
411 self.focus_history.retain(|&id| id != buffer_id);
412 }
413}
414
415impl SplitNode {
416 pub fn leaf(buffer_id: BufferId, split_id: SplitId) -> Self {
418 Self::Leaf {
419 buffer_id,
420 split_id: LeafId(split_id),
421 }
422 }
423
424 pub fn split(
426 direction: SplitDirection,
427 first: SplitNode,
428 second: SplitNode,
429 ratio: f32,
430 split_id: SplitId,
431 ) -> Self {
432 SplitNode::Split {
433 direction,
434 first: Box::new(first),
435 second: Box::new(second),
436 ratio: ratio.clamp(0.1, 0.9), split_id: ContainerId(split_id),
438 }
439 }
440
441 pub fn id(&self) -> SplitId {
443 match self {
444 Self::Leaf { split_id, .. } => split_id.0,
445 Self::Split { split_id, .. } => split_id.0,
446 }
447 }
448
449 pub fn buffer_id(&self) -> Option<BufferId> {
451 match self {
452 Self::Leaf { buffer_id, .. } => Some(*buffer_id),
453 Self::Split { .. } => None,
454 }
455 }
456
457 pub fn find_mut(&mut self, target_id: SplitId) -> Option<&mut Self> {
459 if self.id() == target_id {
460 return Some(self);
461 }
462
463 match self {
464 Self::Leaf { .. } => None,
465 Self::Split { first, second, .. } => first
466 .find_mut(target_id)
467 .or_else(|| second.find_mut(target_id)),
468 }
469 }
470
471 pub fn find(&self, target_id: SplitId) -> Option<&Self> {
473 if self.id() == target_id {
474 return Some(self);
475 }
476
477 match self {
478 Self::Leaf { .. } => None,
479 Self::Split { first, second, .. } => {
480 first.find(target_id).or_else(|| second.find(target_id))
481 }
482 }
483 }
484
485 pub fn parent_container_of(&self, target_id: SplitId) -> Option<ContainerId> {
487 match self {
488 Self::Leaf { .. } => None,
489 Self::Split {
490 split_id,
491 first,
492 second,
493 ..
494 } => {
495 if first.id() == target_id || second.id() == target_id {
496 Some(*split_id)
497 } else {
498 first
499 .parent_container_of(target_id)
500 .or_else(|| second.parent_container_of(target_id))
501 }
502 }
503 }
504 }
505
506 pub fn get_leaves_with_rects(&self, rect: Rect) -> Vec<(LeafId, BufferId, Rect)> {
508 match self {
509 Self::Leaf {
510 buffer_id,
511 split_id,
512 } => {
513 vec![(*split_id, *buffer_id, rect)]
514 }
515 Self::Split {
516 direction,
517 first,
518 second,
519 ratio,
520 ..
521 } => {
522 let (first_rect, second_rect) = split_rect(rect, *direction, *ratio);
523 let mut leaves = first.get_leaves_with_rects(first_rect);
524 leaves.extend(second.get_leaves_with_rects(second_rect));
525 leaves
526 }
527 }
528 }
529
530 pub fn get_separators(&self, rect: Rect) -> Vec<(SplitDirection, u16, u16, u16)> {
533 self.get_separators_with_ids(rect)
534 .into_iter()
535 .map(|(_, dir, x, y, len)| (dir, x, y, len))
536 .collect()
537 }
538
539 pub fn get_separators_with_ids(
542 &self,
543 rect: Rect,
544 ) -> Vec<(ContainerId, SplitDirection, u16, u16, u16)> {
545 match self {
546 Self::Leaf { .. } => vec![],
547 Self::Split {
548 direction,
549 first,
550 second,
551 ratio,
552 split_id,
553 } => {
554 let (first_rect, second_rect) = split_rect(rect, *direction, *ratio);
555 let mut separators = Vec::new();
556
557 match direction {
559 SplitDirection::Horizontal => {
560 separators.push((
563 *split_id,
564 SplitDirection::Horizontal,
565 rect.x,
566 first_rect.y + first_rect.height,
567 rect.width,
568 ));
569 }
570 SplitDirection::Vertical => {
571 separators.push((
574 *split_id,
575 SplitDirection::Vertical,
576 first_rect.x + first_rect.width,
577 rect.y,
578 rect.height,
579 ));
580 }
581 }
582
583 separators.extend(first.get_separators_with_ids(first_rect));
585 separators.extend(second.get_separators_with_ids(second_rect));
586 separators
587 }
588 }
589 }
590
591 pub fn all_split_ids(&self) -> Vec<SplitId> {
593 let mut ids = vec![self.id()];
594 match self {
595 Self::Leaf { .. } => ids,
596 Self::Split { first, second, .. } => {
597 ids.extend(first.all_split_ids());
598 ids.extend(second.all_split_ids());
599 ids
600 }
601 }
602 }
603
604 pub fn leaf_split_ids(&self) -> Vec<LeafId> {
606 match self {
607 Self::Leaf { split_id, .. } => vec![*split_id],
608 Self::Split { first, second, .. } => {
609 let mut ids = first.leaf_split_ids();
610 ids.extend(second.leaf_split_ids());
611 ids
612 }
613 }
614 }
615
616 pub fn count_leaves(&self) -> usize {
618 match self {
619 Self::Leaf { .. } => 1,
620 Self::Split { first, second, .. } => first.count_leaves() + second.count_leaves(),
621 }
622 }
623}
624
625fn split_rect(rect: Rect, direction: SplitDirection, ratio: f32) -> (Rect, Rect) {
628 match direction {
629 SplitDirection::Horizontal => {
630 let total_height = rect.height.saturating_sub(1); let first_height = (total_height as f32 * ratio).round() as u16;
633 let second_height = total_height.saturating_sub(first_height);
634
635 let first = Rect {
636 x: rect.x,
637 y: rect.y,
638 width: rect.width,
639 height: first_height,
640 };
641
642 let second = Rect {
643 x: rect.x,
644 y: rect.y + first_height + 1, width: rect.width,
646 height: second_height,
647 };
648
649 (first, second)
650 }
651 SplitDirection::Vertical => {
652 let total_width = rect.width.saturating_sub(1); let first_width = (total_width as f32 * ratio).round() as u16;
655 let second_width = total_width.saturating_sub(first_width);
656
657 let first = Rect {
658 x: rect.x,
659 y: rect.y,
660 width: first_width,
661 height: rect.height,
662 };
663
664 let second = Rect {
665 x: rect.x + first_width + 1, y: rect.y,
667 width: second_width,
668 height: rect.height,
669 };
670
671 (first, second)
672 }
673 }
674}
675
676#[derive(Debug)]
678pub struct SplitManager {
679 root: SplitNode,
681
682 active_split: LeafId,
684
685 next_split_id: usize,
687
688 maximized_split: Option<SplitId>,
690
691 labels: HashMap<SplitId, String>,
693}
694
695impl SplitManager {
696 pub fn new(buffer_id: BufferId) -> Self {
698 let split_id = SplitId(0);
699 Self {
700 root: SplitNode::leaf(buffer_id, split_id),
701 active_split: LeafId(split_id),
702 next_split_id: 1,
703 maximized_split: None,
704 labels: HashMap::new(),
705 }
706 }
707
708 pub fn root(&self) -> &SplitNode {
710 &self.root
711 }
712
713 pub fn active_split(&self) -> LeafId {
715 self.active_split
716 }
717
718 pub fn set_active_split(&mut self, split_id: LeafId) -> bool {
720 if self.root.find(split_id.into()).is_some() {
722 self.active_split = split_id;
723 true
724 } else {
725 false
726 }
727 }
728
729 pub fn active_buffer_id(&self) -> Option<BufferId> {
731 self.root
732 .find(self.active_split.into())
733 .and_then(|node| node.buffer_id())
734 }
735
736 pub fn get_buffer_id(&self, split_id: SplitId) -> Option<BufferId> {
738 self.root.find(split_id).and_then(|node| node.buffer_id())
739 }
740
741 pub fn set_active_buffer_id(&mut self, new_buffer_id: BufferId) -> bool {
743 if let Some(SplitNode::Leaf { buffer_id, .. }) =
744 self.root.find_mut(self.active_split.into())
745 {
746 *buffer_id = new_buffer_id;
747 return true;
748 }
749 false
750 }
751
752 pub fn set_split_buffer(&mut self, leaf_id: LeafId, new_buffer_id: BufferId) {
754 match self.root.find_mut(leaf_id.into()) {
755 Some(SplitNode::Leaf { buffer_id, .. }) => {
756 *buffer_id = new_buffer_id;
757 }
758 Some(SplitNode::Split { .. }) => {
759 unreachable!("LeafId {:?} points to a container", leaf_id)
760 }
761 None => {
762 unreachable!("LeafId {:?} not found in split tree", leaf_id)
763 }
764 }
765 }
766
767 fn allocate_split_id(&mut self) -> SplitId {
769 let id = SplitId(self.next_split_id);
770 self.next_split_id += 1;
771 id
772 }
773
774 pub fn split_active(
776 &mut self,
777 direction: SplitDirection,
778 new_buffer_id: BufferId,
779 ratio: f32,
780 ) -> Result<LeafId, String> {
781 self.split_active_positioned(direction, new_buffer_id, ratio, false)
782 }
783
784 pub fn split_active_before(
787 &mut self,
788 direction: SplitDirection,
789 new_buffer_id: BufferId,
790 ratio: f32,
791 ) -> Result<LeafId, String> {
792 self.split_active_positioned(direction, new_buffer_id, ratio, true)
793 }
794
795 pub fn split_active_positioned(
796 &mut self,
797 direction: SplitDirection,
798 new_buffer_id: BufferId,
799 ratio: f32,
800 before: bool,
801 ) -> Result<LeafId, String> {
802 let active_id: SplitId = self.active_split.into();
803
804 let result =
806 self.replace_split_with_split(active_id, direction, new_buffer_id, ratio, before);
807
808 if let Ok(new_split_id) = &result {
809 self.active_split = *new_split_id;
811 }
812 result
813 }
814
815 fn replace_split_with_split(
818 &mut self,
819 target_id: SplitId,
820 direction: SplitDirection,
821 new_buffer_id: BufferId,
822 ratio: f32,
823 before: bool,
824 ) -> Result<LeafId, String> {
825 let temp_id = self.allocate_split_id();
827 let new_split_id = self.allocate_split_id();
828 let new_leaf_id = self.allocate_split_id();
829
830 if self.root.id() == target_id {
832 let old_root =
833 std::mem::replace(&mut self.root, SplitNode::leaf(new_buffer_id, temp_id));
834 let new_leaf = SplitNode::leaf(new_buffer_id, new_leaf_id);
835
836 let (first, second) = if before {
837 (new_leaf, old_root)
838 } else {
839 (old_root, new_leaf)
840 };
841
842 self.root = SplitNode::split(direction, first, second, ratio, new_split_id);
843
844 return Ok(LeafId(new_leaf_id));
845 }
846
847 if let Some(node) = self.root.find_mut(target_id) {
849 let old_node = std::mem::replace(node, SplitNode::leaf(new_buffer_id, temp_id));
850 let new_leaf = SplitNode::leaf(new_buffer_id, new_leaf_id);
851
852 let (first, second) = if before {
853 (new_leaf, old_node)
854 } else {
855 (old_node, new_leaf)
856 };
857
858 *node = SplitNode::split(direction, first, second, ratio, new_split_id);
859
860 Ok(LeafId(new_leaf_id))
861 } else {
862 Err(format!("Split {:?} not found", target_id))
863 }
864 }
865
866 pub fn close_split(&mut self, split_id: LeafId) -> Result<(), String> {
868 if self.root.count_leaves() <= 1 {
870 return Err("Cannot close the last split".to_string());
871 }
872
873 if self.root.id() == split_id.into() && self.root.buffer_id().is_some() {
875 return Err("Cannot close the only split".to_string());
876 }
877
878 if self.maximized_split == Some(split_id.into()) {
880 self.maximized_split = None;
881 }
882
883 let removed_ids: Vec<SplitId> = self
885 .root
886 .find(split_id.into())
887 .map(|node| node.all_split_ids())
888 .unwrap_or_default();
889
890 let result = self.remove_split_node(split_id.into());
893
894 if result.is_ok() {
895 for id in &removed_ids {
897 self.labels.remove(id);
898 }
899
900 if self.active_split == split_id {
902 let leaf_ids = self.root.leaf_split_ids();
903 if let Some(&first_leaf) = leaf_ids.first() {
904 self.active_split = first_leaf;
905 }
906 }
907 }
908
909 result
910 }
911
912 fn remove_split_node(&mut self, target_id: SplitId) -> Result<(), String> {
914 if self.root.id() == target_id {
916 if let SplitNode::Split { first, .. } = &self.root {
917 self.root = (**first).clone();
920 return Ok(());
921 }
922 }
923
924 Self::remove_child_static(&mut self.root, target_id)
926 }
927
928 fn remove_child_static(node: &mut SplitNode, target_id: SplitId) -> Result<(), String> {
930 match node {
931 SplitNode::Leaf { .. } => Err("Target not found".to_string()),
932 SplitNode::Split { first, second, .. } => {
933 if first.id() == target_id {
935 *node = (**second).clone();
937 Ok(())
938 } else if second.id() == target_id {
939 *node = (**first).clone();
941 Ok(())
942 } else {
943 Self::remove_child_static(first, target_id)
945 .or_else(|_| Self::remove_child_static(second, target_id))
946 }
947 }
948 }
949 }
950
951 pub fn adjust_ratio(&mut self, container_id: ContainerId, delta: f32) {
953 match self.root.find_mut(container_id.into()) {
954 Some(SplitNode::Split { ratio, .. }) => {
955 *ratio = (*ratio + delta).clamp(0.1, 0.9);
956 }
957 Some(SplitNode::Leaf { .. }) => {
958 unreachable!("ContainerId {:?} points to a leaf", container_id)
959 }
960 None => {
961 unreachable!("ContainerId {:?} not found in split tree", container_id)
962 }
963 }
964 }
965
966 pub fn parent_container_of(&self, leaf_id: LeafId) -> Option<ContainerId> {
968 self.root.parent_container_of(leaf_id.into())
969 }
970
971 pub fn get_visible_buffers(&self, viewport_rect: Rect) -> Vec<(LeafId, BufferId, Rect)> {
973 if let Some(maximized_id) = self.maximized_split {
975 if let Some(node) = self.root.find(maximized_id) {
976 if let SplitNode::Leaf {
977 buffer_id,
978 split_id,
979 } = node
980 {
981 return vec![(*split_id, *buffer_id, viewport_rect)];
982 }
983 }
984 }
986 self.root.get_leaves_with_rects(viewport_rect)
987 }
988
989 pub fn get_separators(&self, viewport_rect: Rect) -> Vec<(SplitDirection, u16, u16, u16)> {
992 if self.maximized_split.is_some() {
994 return vec![];
995 }
996 self.root.get_separators(viewport_rect)
997 }
998
999 pub fn get_separators_with_ids(
1002 &self,
1003 viewport_rect: Rect,
1004 ) -> Vec<(ContainerId, SplitDirection, u16, u16, u16)> {
1005 if self.maximized_split.is_some() {
1007 return vec![];
1008 }
1009 self.root.get_separators_with_ids(viewport_rect)
1010 }
1011
1012 pub fn get_ratio(&self, split_id: SplitId) -> Option<f32> {
1014 if let Some(SplitNode::Split { ratio, .. }) = self.root.find(split_id) {
1015 Some(*ratio)
1016 } else {
1017 None
1018 }
1019 }
1020
1021 pub fn set_ratio(&mut self, container_id: ContainerId, new_ratio: f32) {
1023 match self.root.find_mut(container_id.into()) {
1024 Some(SplitNode::Split { ratio, .. }) => {
1025 *ratio = new_ratio.clamp(0.1, 0.9);
1026 }
1027 Some(SplitNode::Leaf { .. }) => {
1028 unreachable!("ContainerId {:?} points to a leaf", container_id)
1029 }
1030 None => {
1031 unreachable!("ContainerId {:?} not found in split tree", container_id)
1032 }
1033 }
1034 }
1035
1036 pub fn distribute_splits_evenly(&mut self) {
1039 Self::distribute_node_evenly(&mut self.root);
1040 }
1041
1042 fn distribute_node_evenly(node: &mut SplitNode) -> usize {
1045 match node {
1046 SplitNode::Leaf { .. } => 1,
1047 SplitNode::Split {
1048 first,
1049 second,
1050 ratio,
1051 ..
1052 } => {
1053 let first_leaves = Self::distribute_node_evenly(first);
1054 let second_leaves = Self::distribute_node_evenly(second);
1055 let total_leaves = first_leaves + second_leaves;
1056
1057 *ratio = (first_leaves as f32 / total_leaves as f32).clamp(0.1, 0.9);
1060
1061 total_leaves
1062 }
1063 }
1064 }
1065
1066 pub fn next_split(&mut self) {
1068 let leaf_ids = self.root.leaf_split_ids();
1069 if let Some(pos) = leaf_ids.iter().position(|id| *id == self.active_split) {
1070 let next_pos = (pos + 1) % leaf_ids.len();
1071 self.active_split = leaf_ids[next_pos];
1072 }
1073 }
1074
1075 pub fn prev_split(&mut self) {
1077 let leaf_ids = self.root.leaf_split_ids();
1078 if let Some(pos) = leaf_ids.iter().position(|id| *id == self.active_split) {
1079 let prev_pos = if pos == 0 { leaf_ids.len() } else { pos } - 1;
1080 self.active_split = leaf_ids[prev_pos];
1081 }
1082 }
1083
1084 pub fn splits_for_buffer(&self, target_buffer_id: BufferId) -> Vec<LeafId> {
1086 self.root
1087 .get_leaves_with_rects(Rect {
1088 x: 0,
1089 y: 0,
1090 width: 1,
1091 height: 1,
1092 })
1093 .into_iter()
1094 .filter(|(_, buffer_id, _)| *buffer_id == target_buffer_id)
1095 .map(|(split_id, _, _)| split_id)
1096 .collect()
1097 }
1098
1099 pub fn buffer_for_split(&self, target_split_id: LeafId) -> Option<BufferId> {
1101 self.root
1102 .get_leaves_with_rects(Rect {
1103 x: 0,
1104 y: 0,
1105 width: 1,
1106 height: 1,
1107 })
1108 .into_iter()
1109 .find(|(split_id, _, _)| *split_id == target_split_id)
1110 .map(|(_, buffer_id, _)| buffer_id)
1111 }
1112
1113 pub fn maximize_split(&mut self) -> Result<(), String> {
1116 if self.root.count_leaves() <= 1 {
1118 return Err("Cannot maximize: only one split exists".to_string());
1119 }
1120
1121 if self.maximized_split.is_some() {
1123 return Err("A split is already maximized".to_string());
1124 }
1125
1126 self.maximized_split = Some(self.active_split.into());
1128 Ok(())
1129 }
1130
1131 pub fn unmaximize_split(&mut self) -> Result<(), String> {
1134 if self.maximized_split.is_none() {
1135 return Err("No split is maximized".to_string());
1136 }
1137
1138 self.maximized_split = None;
1139 Ok(())
1140 }
1141
1142 pub fn is_maximized(&self) -> bool {
1144 self.maximized_split.is_some()
1145 }
1146
1147 pub fn maximized_split(&self) -> Option<SplitId> {
1149 self.maximized_split
1150 }
1151
1152 pub fn toggle_maximize(&mut self) -> Result<bool, String> {
1156 if self.is_maximized() {
1157 self.unmaximize_split()?;
1158 Ok(false)
1159 } else {
1160 self.maximize_split()?;
1161 Ok(true)
1162 }
1163 }
1164
1165 pub fn get_splits_in_group(
1167 &self,
1168 group_id: u32,
1169 view_states: &std::collections::HashMap<LeafId, SplitViewState>,
1170 ) -> Vec<LeafId> {
1171 self.root
1172 .leaf_split_ids()
1173 .into_iter()
1174 .filter(|id| {
1175 view_states
1176 .get(id)
1177 .and_then(|vs| vs.sync_group)
1178 .is_some_and(|g| g == group_id)
1179 })
1180 .collect()
1181 }
1182
1183 pub fn set_label(&mut self, split_id: LeafId, label: String) {
1187 self.labels.insert(split_id.into(), label);
1188 }
1189
1190 pub fn clear_label(&mut self, split_id: SplitId) {
1192 self.labels.remove(&split_id);
1193 }
1194
1195 pub fn get_label(&self, split_id: SplitId) -> Option<&str> {
1197 self.labels.get(&split_id).map(|s| s.as_str())
1198 }
1199
1200 pub fn labels(&self) -> &HashMap<SplitId, String> {
1202 &self.labels
1203 }
1204
1205 pub fn find_split_by_label(&self, label: &str) -> Option<LeafId> {
1207 self.root
1208 .leaf_split_ids()
1209 .into_iter()
1210 .find(|id| self.labels.get(&(*id).into()).is_some_and(|l| l == label))
1211 }
1212
1213 pub fn find_unlabeled_leaf(&self) -> Option<LeafId> {
1215 self.root
1216 .leaf_split_ids()
1217 .into_iter()
1218 .find(|id| !self.labels.contains_key(&(*id).into()))
1219 }
1220}
1221
1222#[cfg(test)]
1223mod tests {
1224 use super::*;
1225
1226 #[test]
1227 fn test_create_split_manager() {
1228 let buffer_id = BufferId(0);
1229 let manager = SplitManager::new(buffer_id);
1230
1231 assert_eq!(manager.active_buffer_id(), Some(buffer_id));
1232 assert_eq!(manager.root().count_leaves(), 1);
1233 }
1234
1235 #[test]
1236 fn test_horizontal_split() {
1237 let buffer_a = BufferId(0);
1238 let buffer_b = BufferId(1);
1239
1240 let mut manager = SplitManager::new(buffer_a);
1241 let result = manager.split_active(SplitDirection::Horizontal, buffer_b, 0.5);
1242
1243 assert!(result.is_ok());
1244 assert_eq!(manager.root().count_leaves(), 2);
1245 }
1246
1247 #[test]
1248 fn test_vertical_split() {
1249 let buffer_a = BufferId(0);
1250 let buffer_b = BufferId(1);
1251
1252 let mut manager = SplitManager::new(buffer_a);
1253 let result = manager.split_active(SplitDirection::Vertical, buffer_b, 0.5);
1254
1255 assert!(result.is_ok());
1256 assert_eq!(manager.root().count_leaves(), 2);
1257 }
1258
1259 #[test]
1260 fn test_nested_splits() {
1261 let buffer_a = BufferId(0);
1262 let buffer_b = BufferId(1);
1263 let buffer_c = BufferId(2);
1264
1265 let mut manager = SplitManager::new(buffer_a);
1266
1267 manager
1269 .split_active(SplitDirection::Horizontal, buffer_b, 0.5)
1270 .unwrap();
1271
1272 manager
1274 .split_active(SplitDirection::Vertical, buffer_c, 0.5)
1275 .unwrap();
1276
1277 assert_eq!(manager.root().count_leaves(), 3);
1278 }
1279
1280 #[test]
1281 fn test_close_split() {
1282 let buffer_a = BufferId(0);
1283 let buffer_b = BufferId(1);
1284
1285 let mut manager = SplitManager::new(buffer_a);
1286 let new_split = manager
1287 .split_active(SplitDirection::Horizontal, buffer_b, 0.5)
1288 .unwrap();
1289
1290 assert_eq!(manager.root().count_leaves(), 2);
1291
1292 let result = manager.close_split(new_split);
1294 assert!(result.is_ok());
1295 assert_eq!(manager.root().count_leaves(), 1);
1296 }
1297
1298 #[test]
1299 fn test_cannot_close_last_split() {
1300 let buffer_a = BufferId(0);
1301 let mut manager = SplitManager::new(buffer_a);
1302
1303 let result = manager.close_split(manager.active_split());
1304 assert!(result.is_err());
1305 }
1306
1307 #[test]
1308 fn test_split_rect_horizontal() {
1309 let rect = Rect {
1310 x: 0,
1311 y: 0,
1312 width: 100,
1313 height: 100,
1314 };
1315
1316 let (first, second) = split_rect(rect, SplitDirection::Horizontal, 0.5);
1317
1318 assert_eq!(first.height, 50);
1320 assert_eq!(second.height, 49);
1321 assert_eq!(first.width, 100);
1322 assert_eq!(second.width, 100);
1323 assert_eq!(first.y, 0);
1324 assert_eq!(second.y, 51); }
1326
1327 #[test]
1328 fn test_split_rect_vertical() {
1329 let rect = Rect {
1330 x: 0,
1331 y: 0,
1332 width: 100,
1333 height: 100,
1334 };
1335
1336 let (first, second) = split_rect(rect, SplitDirection::Vertical, 0.5);
1337
1338 assert_eq!(first.width, 50);
1340 assert_eq!(second.width, 49);
1341 assert_eq!(first.height, 100);
1342 assert_eq!(second.height, 100);
1343 assert_eq!(first.x, 0);
1344 assert_eq!(second.x, 51); }
1346
1347 #[test]
1350 fn test_set_and_get_label() {
1351 let mut manager = SplitManager::new(BufferId(0));
1352 let split = manager.active_split();
1353
1354 assert_eq!(manager.get_label(split.into()), None);
1355
1356 manager.set_label(split, "sidebar".to_string());
1357 assert_eq!(manager.get_label(split.into()), Some("sidebar"));
1358 }
1359
1360 #[test]
1361 fn test_clear_label() {
1362 let mut manager = SplitManager::new(BufferId(0));
1363 let split = manager.active_split();
1364
1365 manager.set_label(split, "sidebar".to_string());
1366 assert!(manager.get_label(split.into()).is_some());
1367
1368 manager.clear_label(split.into());
1369 assert_eq!(manager.get_label(split.into()), None);
1370 }
1371
1372 #[test]
1373 fn test_find_split_by_label() {
1374 let mut manager = SplitManager::new(BufferId(0));
1375 let first_split = manager.active_split();
1376
1377 let second_split = manager
1378 .split_active(SplitDirection::Vertical, BufferId(1), 0.5)
1379 .unwrap();
1380
1381 manager.set_label(first_split, "sidebar".to_string());
1382
1383 assert_eq!(manager.find_split_by_label("sidebar"), Some(first_split));
1384 assert_eq!(manager.find_split_by_label("terminal"), None);
1385
1386 assert_ne!(manager.find_split_by_label("sidebar"), Some(second_split));
1388 }
1389
1390 #[test]
1391 fn test_find_unlabeled_leaf() {
1392 let mut manager = SplitManager::new(BufferId(0));
1393 let first_split = manager.active_split();
1394
1395 let second_split = manager
1396 .split_active(SplitDirection::Vertical, BufferId(1), 0.5)
1397 .unwrap();
1398
1399 assert!(manager.find_unlabeled_leaf().is_some());
1401
1402 manager.set_label(first_split, "sidebar".to_string());
1404 assert_eq!(manager.find_unlabeled_leaf(), Some(second_split));
1405
1406 manager.set_label(second_split, "terminal".to_string());
1408 assert_eq!(manager.find_unlabeled_leaf(), None);
1409 }
1410
1411 #[test]
1412 fn test_close_split_cleans_up_label() {
1413 let mut manager = SplitManager::new(BufferId(0));
1414 let _first_split = manager.active_split();
1415
1416 let second_split = manager
1417 .split_active(SplitDirection::Vertical, BufferId(1), 0.5)
1418 .unwrap();
1419
1420 manager.set_label(second_split, "sidebar".to_string());
1421 assert_eq!(manager.find_split_by_label("sidebar"), Some(second_split));
1422
1423 manager.close_split(second_split).unwrap();
1424
1425 assert_eq!(manager.find_split_by_label("sidebar"), None);
1427 assert_eq!(manager.get_label(second_split.into()), None);
1428 }
1429
1430 #[test]
1431 fn test_label_overwrite() {
1432 let mut manager = SplitManager::new(BufferId(0));
1433 let split = manager.active_split();
1434
1435 manager.set_label(split, "sidebar".to_string());
1436 assert_eq!(manager.get_label(split.into()), Some("sidebar"));
1437
1438 manager.set_label(split, "terminal".to_string());
1439 assert_eq!(manager.get_label(split.into()), Some("terminal"));
1440 assert_eq!(manager.find_split_by_label("sidebar"), None);
1441 assert_eq!(manager.find_split_by_label("terminal"), Some(split));
1442 }
1443
1444 #[test]
1445 fn test_find_unlabeled_leaf_single_split_no_label() {
1446 let manager = SplitManager::new(BufferId(0));
1447 assert_eq!(manager.find_unlabeled_leaf(), Some(manager.active_split()));
1449 }
1450
1451 #[test]
1452 fn test_find_unlabeled_leaf_single_split_labeled() {
1453 let mut manager = SplitManager::new(BufferId(0));
1454 let split = manager.active_split();
1455 manager.set_label(split, "only".to_string());
1456 assert_eq!(manager.find_unlabeled_leaf(), None);
1458 }
1459}