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 highlight_current_line: bool,
100
101 pub view_transform: Option<ViewTransformPayload>,
103
104 pub view_transform_stale: bool,
108
109 pub plugin_state: std::collections::HashMap<String, serde_json::Value>,
113
114 pub folds: FoldManager,
116}
117
118impl BufferViewState {
119 pub fn ensure_cursor_visible(&mut self, buffer: &mut Buffer, marker_list: &MarkerList) {
125 let hidden: Vec<(usize, usize)> = self
126 .folds
127 .resolved_ranges(buffer, marker_list)
128 .into_iter()
129 .map(|r| (r.start_byte, r.end_byte))
130 .collect();
131 let cursor = *self.cursors.primary();
132 self.viewport.ensure_visible(buffer, &cursor, &hidden);
133 }
134
135 pub fn new(width: u16, height: u16) -> Self {
137 Self {
138 cursors: Cursors::new(),
139 viewport: Viewport::new(width, height),
140 view_mode: ViewMode::Source,
141 compose_width: None,
142 compose_column_guides: None,
143 rulers: Vec::new(),
144 show_line_numbers: true,
145 highlight_current_line: true,
146 view_transform: None,
147 view_transform_stale: false,
148 plugin_state: std::collections::HashMap::new(),
149 folds: FoldManager::new(),
150 }
151 }
152
153 pub fn apply_config_defaults(
160 &mut self,
161 line_numbers: bool,
162 highlight_current_line: bool,
163 line_wrap: bool,
164 wrap_indent: bool,
165 wrap_column: Option<usize>,
166 rulers: Vec<usize>,
167 ) {
168 self.show_line_numbers = line_numbers;
169 self.highlight_current_line = highlight_current_line;
170 self.viewport.line_wrap_enabled = line_wrap;
171 self.viewport.wrap_indent = wrap_indent;
172 self.viewport.wrap_column = wrap_column;
173 self.rulers = rulers;
174 }
175
176 pub fn activate_page_view(&mut self, page_width: Option<usize>) {
182 self.view_mode = ViewMode::PageView;
183 self.show_line_numbers = false;
184 self.viewport.line_wrap_enabled = false;
185 if let Some(width) = page_width {
186 self.compose_width = Some(width as u16);
187 }
188 }
189}
190
191impl Clone for BufferViewState {
192 fn clone(&self) -> Self {
193 Self {
194 cursors: self.cursors.clone(),
195 viewport: self.viewport.clone(),
196 view_mode: self.view_mode.clone(),
197 compose_width: self.compose_width,
198 compose_column_guides: self.compose_column_guides.clone(),
199 rulers: self.rulers.clone(),
200 show_line_numbers: self.show_line_numbers,
201 highlight_current_line: self.highlight_current_line,
202 view_transform: self.view_transform.clone(),
203 view_transform_stale: self.view_transform_stale,
204 plugin_state: self.plugin_state.clone(),
205 folds: FoldManager::new(),
207 }
208 }
209}
210
211#[derive(Debug, Clone)]
223pub struct SplitViewState {
224 pub active_buffer: BufferId,
226
227 pub keyed_states: HashMap<BufferId, BufferViewState>,
229
230 pub open_buffers: Vec<BufferId>,
233
234 pub tab_scroll_offset: usize,
236
237 pub layout: Option<Layout>,
240
241 pub layout_dirty: bool,
243
244 pub focus_history: Vec<BufferId>,
247
248 pub sync_group: Option<u32>,
251
252 pub composite_view: Option<BufferId>,
257}
258
259impl std::ops::Deref for SplitViewState {
260 type Target = BufferViewState;
261
262 fn deref(&self) -> &BufferViewState {
263 self.active_state()
264 }
265}
266
267impl std::ops::DerefMut for SplitViewState {
268 fn deref_mut(&mut self) -> &mut BufferViewState {
269 self.active_state_mut()
270 }
271}
272
273impl SplitViewState {
274 pub fn with_buffer(width: u16, height: u16, buffer_id: BufferId) -> Self {
276 let buf_state = BufferViewState::new(width, height);
277 let mut keyed_states = HashMap::new();
278 keyed_states.insert(buffer_id, buf_state);
279 Self {
280 active_buffer: buffer_id,
281 keyed_states,
282 open_buffers: vec![buffer_id],
283 tab_scroll_offset: 0,
284 layout: None,
285 layout_dirty: true,
286 focus_history: Vec::new(),
287 sync_group: None,
288 composite_view: None,
289 }
290 }
291
292 pub fn active_state(&self) -> &BufferViewState {
294 self.keyed_states
295 .get(&self.active_buffer)
296 .expect("active_buffer must always have an entry in keyed_states")
297 }
298
299 pub fn active_state_mut(&mut self) -> &mut BufferViewState {
301 self.keyed_states
302 .get_mut(&self.active_buffer)
303 .expect("active_buffer must always have an entry in keyed_states")
304 }
305
306 pub fn switch_buffer(&mut self, new_buffer_id: BufferId) {
312 if new_buffer_id == self.active_buffer {
313 return;
314 }
315 if !self.keyed_states.contains_key(&new_buffer_id) {
317 let active = self.active_state();
318 let width = active.viewport.width;
319 let height = active.viewport.height;
320 self.keyed_states
321 .insert(new_buffer_id, BufferViewState::new(width, height));
322 }
323 self.active_buffer = new_buffer_id;
324 self.layout_dirty = true;
326 }
327
328 pub fn buffer_state(&self, buffer_id: BufferId) -> Option<&BufferViewState> {
330 self.keyed_states.get(&buffer_id)
331 }
332
333 pub fn buffer_state_mut(&mut self, buffer_id: BufferId) -> Option<&mut BufferViewState> {
335 self.keyed_states.get_mut(&buffer_id)
336 }
337
338 pub fn ensure_buffer_state(&mut self, buffer_id: BufferId) -> &mut BufferViewState {
341 let (width, height) = {
342 let active = self.active_state();
343 (active.viewport.width, active.viewport.height)
344 };
345 self.keyed_states
346 .entry(buffer_id)
347 .or_insert_with(|| BufferViewState::new(width, height))
348 }
349
350 pub fn remove_buffer_state(&mut self, buffer_id: BufferId) {
352 if buffer_id != self.active_buffer {
353 self.keyed_states.remove(&buffer_id);
354 }
355 }
356
357 pub fn invalidate_layout(&mut self) {
359 self.layout_dirty = true;
360 }
361
362 pub fn ensure_layout(
370 &mut self,
371 tokens: &[fresh_core::api::ViewTokenWire],
372 source_range: std::ops::Range<usize>,
373 tab_size: usize,
374 ) -> &Layout {
375 if self.layout.is_none() || self.layout_dirty {
376 self.layout = Some(Layout::from_tokens(tokens, source_range, tab_size));
377 self.layout_dirty = false;
378 }
379 self.layout.as_ref().unwrap()
380 }
381
382 pub fn get_layout(&self) -> Option<&Layout> {
384 if self.layout_dirty {
385 None
386 } else {
387 self.layout.as_ref()
388 }
389 }
390
391 pub fn add_buffer(&mut self, buffer_id: BufferId) {
393 if !self.open_buffers.contains(&buffer_id) {
394 self.open_buffers.push(buffer_id);
395 }
396 }
397
398 pub fn remove_buffer(&mut self, buffer_id: BufferId) {
400 self.open_buffers.retain(|&id| id != buffer_id);
401 if buffer_id != self.active_buffer {
403 self.keyed_states.remove(&buffer_id);
404 }
405 }
406
407 pub fn has_buffer(&self, buffer_id: BufferId) -> bool {
409 self.open_buffers.contains(&buffer_id)
410 }
411
412 pub fn push_focus(&mut self, buffer_id: BufferId) {
415 self.focus_history.retain(|&id| id != buffer_id);
417 self.focus_history.push(buffer_id);
418 if self.focus_history.len() > 50 {
420 self.focus_history.remove(0);
421 }
422 }
423
424 pub fn previous_buffer(&self) -> Option<BufferId> {
426 self.focus_history.last().copied()
427 }
428
429 pub fn pop_focus(&mut self) -> Option<BufferId> {
431 self.focus_history.pop()
432 }
433
434 pub fn remove_from_history(&mut self, buffer_id: BufferId) {
436 self.focus_history.retain(|&id| id != buffer_id);
437 }
438}
439
440impl SplitNode {
441 pub fn leaf(buffer_id: BufferId, split_id: SplitId) -> Self {
443 Self::Leaf {
444 buffer_id,
445 split_id: LeafId(split_id),
446 }
447 }
448
449 pub fn split(
451 direction: SplitDirection,
452 first: SplitNode,
453 second: SplitNode,
454 ratio: f32,
455 split_id: SplitId,
456 ) -> Self {
457 SplitNode::Split {
458 direction,
459 first: Box::new(first),
460 second: Box::new(second),
461 ratio: ratio.clamp(0.1, 0.9), split_id: ContainerId(split_id),
463 }
464 }
465
466 pub fn id(&self) -> SplitId {
468 match self {
469 Self::Leaf { split_id, .. } => split_id.0,
470 Self::Split { split_id, .. } => split_id.0,
471 }
472 }
473
474 pub fn buffer_id(&self) -> Option<BufferId> {
476 match self {
477 Self::Leaf { buffer_id, .. } => Some(*buffer_id),
478 Self::Split { .. } => None,
479 }
480 }
481
482 pub fn find_mut(&mut self, target_id: SplitId) -> Option<&mut Self> {
484 if self.id() == target_id {
485 return Some(self);
486 }
487
488 match self {
489 Self::Leaf { .. } => None,
490 Self::Split { first, second, .. } => first
491 .find_mut(target_id)
492 .or_else(|| second.find_mut(target_id)),
493 }
494 }
495
496 pub fn find(&self, target_id: SplitId) -> Option<&Self> {
498 if self.id() == target_id {
499 return Some(self);
500 }
501
502 match self {
503 Self::Leaf { .. } => None,
504 Self::Split { first, second, .. } => {
505 first.find(target_id).or_else(|| second.find(target_id))
506 }
507 }
508 }
509
510 pub fn parent_container_of(&self, target_id: SplitId) -> Option<ContainerId> {
512 match self {
513 Self::Leaf { .. } => None,
514 Self::Split {
515 split_id,
516 first,
517 second,
518 ..
519 } => {
520 if first.id() == target_id || second.id() == target_id {
521 Some(*split_id)
522 } else {
523 first
524 .parent_container_of(target_id)
525 .or_else(|| second.parent_container_of(target_id))
526 }
527 }
528 }
529 }
530
531 pub fn get_leaves_with_rects(&self, rect: Rect) -> Vec<(LeafId, BufferId, Rect)> {
533 match self {
534 Self::Leaf {
535 buffer_id,
536 split_id,
537 } => {
538 vec![(*split_id, *buffer_id, rect)]
539 }
540 Self::Split {
541 direction,
542 first,
543 second,
544 ratio,
545 ..
546 } => {
547 let (first_rect, second_rect) = split_rect(rect, *direction, *ratio);
548 let mut leaves = first.get_leaves_with_rects(first_rect);
549 leaves.extend(second.get_leaves_with_rects(second_rect));
550 leaves
551 }
552 }
553 }
554
555 pub fn get_separators(&self, rect: Rect) -> Vec<(SplitDirection, u16, u16, u16)> {
558 self.get_separators_with_ids(rect)
559 .into_iter()
560 .map(|(_, dir, x, y, len)| (dir, x, y, len))
561 .collect()
562 }
563
564 pub fn get_separators_with_ids(
567 &self,
568 rect: Rect,
569 ) -> Vec<(ContainerId, SplitDirection, u16, u16, u16)> {
570 match self {
571 Self::Leaf { .. } => vec![],
572 Self::Split {
573 direction,
574 first,
575 second,
576 ratio,
577 split_id,
578 } => {
579 let (first_rect, second_rect) = split_rect(rect, *direction, *ratio);
580 let mut separators = Vec::new();
581
582 match direction {
584 SplitDirection::Horizontal => {
585 separators.push((
588 *split_id,
589 SplitDirection::Horizontal,
590 rect.x,
591 first_rect.y + first_rect.height,
592 rect.width,
593 ));
594 }
595 SplitDirection::Vertical => {
596 separators.push((
599 *split_id,
600 SplitDirection::Vertical,
601 first_rect.x + first_rect.width,
602 rect.y,
603 rect.height,
604 ));
605 }
606 }
607
608 separators.extend(first.get_separators_with_ids(first_rect));
610 separators.extend(second.get_separators_with_ids(second_rect));
611 separators
612 }
613 }
614 }
615
616 pub fn all_split_ids(&self) -> Vec<SplitId> {
618 let mut ids = vec![self.id()];
619 match self {
620 Self::Leaf { .. } => ids,
621 Self::Split { first, second, .. } => {
622 ids.extend(first.all_split_ids());
623 ids.extend(second.all_split_ids());
624 ids
625 }
626 }
627 }
628
629 pub fn leaf_split_ids(&self) -> Vec<LeafId> {
631 match self {
632 Self::Leaf { split_id, .. } => vec![*split_id],
633 Self::Split { first, second, .. } => {
634 let mut ids = first.leaf_split_ids();
635 ids.extend(second.leaf_split_ids());
636 ids
637 }
638 }
639 }
640
641 pub fn count_leaves(&self) -> usize {
643 match self {
644 Self::Leaf { .. } => 1,
645 Self::Split { first, second, .. } => first.count_leaves() + second.count_leaves(),
646 }
647 }
648}
649
650fn split_rect(rect: Rect, direction: SplitDirection, ratio: f32) -> (Rect, Rect) {
653 match direction {
654 SplitDirection::Horizontal => {
655 let total_height = rect.height.saturating_sub(1); let first_height = (total_height as f32 * ratio).round() as u16;
658 let second_height = total_height.saturating_sub(first_height);
659
660 let first = Rect {
661 x: rect.x,
662 y: rect.y,
663 width: rect.width,
664 height: first_height,
665 };
666
667 let second = Rect {
668 x: rect.x,
669 y: rect.y + first_height + 1, width: rect.width,
671 height: second_height,
672 };
673
674 (first, second)
675 }
676 SplitDirection::Vertical => {
677 let total_width = rect.width.saturating_sub(1); let first_width = (total_width as f32 * ratio).round() as u16;
680 let second_width = total_width.saturating_sub(first_width);
681
682 let first = Rect {
683 x: rect.x,
684 y: rect.y,
685 width: first_width,
686 height: rect.height,
687 };
688
689 let second = Rect {
690 x: rect.x + first_width + 1, y: rect.y,
692 width: second_width,
693 height: rect.height,
694 };
695
696 (first, second)
697 }
698 }
699}
700
701#[derive(Debug)]
703pub struct SplitManager {
704 root: SplitNode,
706
707 active_split: LeafId,
709
710 next_split_id: usize,
712
713 maximized_split: Option<SplitId>,
715
716 labels: HashMap<SplitId, String>,
718}
719
720impl SplitManager {
721 pub fn new(buffer_id: BufferId) -> Self {
723 let split_id = SplitId(0);
724 Self {
725 root: SplitNode::leaf(buffer_id, split_id),
726 active_split: LeafId(split_id),
727 next_split_id: 1,
728 maximized_split: None,
729 labels: HashMap::new(),
730 }
731 }
732
733 pub fn root(&self) -> &SplitNode {
735 &self.root
736 }
737
738 pub fn active_split(&self) -> LeafId {
740 self.active_split
741 }
742
743 pub fn set_active_split(&mut self, split_id: LeafId) -> bool {
745 if self.root.find(split_id.into()).is_some() {
747 self.active_split = split_id;
748 true
749 } else {
750 false
751 }
752 }
753
754 pub fn active_buffer_id(&self) -> Option<BufferId> {
756 self.root
757 .find(self.active_split.into())
758 .and_then(|node| node.buffer_id())
759 }
760
761 pub fn get_buffer_id(&self, split_id: SplitId) -> Option<BufferId> {
763 self.root.find(split_id).and_then(|node| node.buffer_id())
764 }
765
766 pub fn set_active_buffer_id(&mut self, new_buffer_id: BufferId) -> bool {
768 if let Some(SplitNode::Leaf { buffer_id, .. }) =
769 self.root.find_mut(self.active_split.into())
770 {
771 *buffer_id = new_buffer_id;
772 return true;
773 }
774 false
775 }
776
777 pub fn set_split_buffer(&mut self, leaf_id: LeafId, new_buffer_id: BufferId) {
779 match self.root.find_mut(leaf_id.into()) {
780 Some(SplitNode::Leaf { buffer_id, .. }) => {
781 *buffer_id = new_buffer_id;
782 }
783 Some(SplitNode::Split { .. }) => {
784 unreachable!("LeafId {:?} points to a container", leaf_id)
785 }
786 None => {
787 unreachable!("LeafId {:?} not found in split tree", leaf_id)
788 }
789 }
790 }
791
792 fn allocate_split_id(&mut self) -> SplitId {
794 let id = SplitId(self.next_split_id);
795 self.next_split_id += 1;
796 id
797 }
798
799 pub fn split_active(
801 &mut self,
802 direction: SplitDirection,
803 new_buffer_id: BufferId,
804 ratio: f32,
805 ) -> Result<LeafId, String> {
806 self.split_active_positioned(direction, new_buffer_id, ratio, false)
807 }
808
809 pub fn split_active_before(
812 &mut self,
813 direction: SplitDirection,
814 new_buffer_id: BufferId,
815 ratio: f32,
816 ) -> Result<LeafId, String> {
817 self.split_active_positioned(direction, new_buffer_id, ratio, true)
818 }
819
820 pub fn split_active_positioned(
821 &mut self,
822 direction: SplitDirection,
823 new_buffer_id: BufferId,
824 ratio: f32,
825 before: bool,
826 ) -> Result<LeafId, String> {
827 let active_id: SplitId = self.active_split.into();
828
829 let result =
831 self.replace_split_with_split(active_id, direction, new_buffer_id, ratio, before);
832
833 if let Ok(new_split_id) = &result {
834 self.active_split = *new_split_id;
836 }
837 result
838 }
839
840 fn replace_split_with_split(
843 &mut self,
844 target_id: SplitId,
845 direction: SplitDirection,
846 new_buffer_id: BufferId,
847 ratio: f32,
848 before: bool,
849 ) -> Result<LeafId, String> {
850 let temp_id = self.allocate_split_id();
852 let new_split_id = self.allocate_split_id();
853 let new_leaf_id = self.allocate_split_id();
854
855 if self.root.id() == target_id {
857 let old_root =
858 std::mem::replace(&mut self.root, SplitNode::leaf(new_buffer_id, temp_id));
859 let new_leaf = SplitNode::leaf(new_buffer_id, new_leaf_id);
860
861 let (first, second) = if before {
862 (new_leaf, old_root)
863 } else {
864 (old_root, new_leaf)
865 };
866
867 self.root = SplitNode::split(direction, first, second, ratio, new_split_id);
868
869 return Ok(LeafId(new_leaf_id));
870 }
871
872 if let Some(node) = self.root.find_mut(target_id) {
874 let old_node = std::mem::replace(node, SplitNode::leaf(new_buffer_id, temp_id));
875 let new_leaf = SplitNode::leaf(new_buffer_id, new_leaf_id);
876
877 let (first, second) = if before {
878 (new_leaf, old_node)
879 } else {
880 (old_node, new_leaf)
881 };
882
883 *node = SplitNode::split(direction, first, second, ratio, new_split_id);
884
885 Ok(LeafId(new_leaf_id))
886 } else {
887 Err(format!("Split {:?} not found", target_id))
888 }
889 }
890
891 pub fn close_split(&mut self, split_id: LeafId) -> Result<(), String> {
893 if self.root.count_leaves() <= 1 {
895 return Err("Cannot close the last split".to_string());
896 }
897
898 if self.root.id() == split_id.into() && self.root.buffer_id().is_some() {
900 return Err("Cannot close the only split".to_string());
901 }
902
903 if self.maximized_split == Some(split_id.into()) {
905 self.maximized_split = None;
906 }
907
908 let removed_ids: Vec<SplitId> = self
910 .root
911 .find(split_id.into())
912 .map(|node| node.all_split_ids())
913 .unwrap_or_default();
914
915 let result = self.remove_split_node(split_id.into());
918
919 if result.is_ok() {
920 for id in &removed_ids {
922 self.labels.remove(id);
923 }
924
925 if self.active_split == split_id {
927 let leaf_ids = self.root.leaf_split_ids();
928 if let Some(&first_leaf) = leaf_ids.first() {
929 self.active_split = first_leaf;
930 }
931 }
932 }
933
934 result
935 }
936
937 fn remove_split_node(&mut self, target_id: SplitId) -> Result<(), String> {
939 if self.root.id() == target_id {
941 if let SplitNode::Split { first, .. } = &self.root {
942 self.root = (**first).clone();
945 return Ok(());
946 }
947 }
948
949 Self::remove_child_static(&mut self.root, target_id)
951 }
952
953 fn remove_child_static(node: &mut SplitNode, target_id: SplitId) -> Result<(), String> {
955 match node {
956 SplitNode::Leaf { .. } => Err("Target not found".to_string()),
957 SplitNode::Split { first, second, .. } => {
958 if first.id() == target_id {
960 *node = (**second).clone();
962 Ok(())
963 } else if second.id() == target_id {
964 *node = (**first).clone();
966 Ok(())
967 } else {
968 Self::remove_child_static(first, target_id)
970 .or_else(|_| Self::remove_child_static(second, target_id))
971 }
972 }
973 }
974 }
975
976 pub fn adjust_ratio(&mut self, container_id: ContainerId, delta: f32) {
978 match self.root.find_mut(container_id.into()) {
979 Some(SplitNode::Split { ratio, .. }) => {
980 *ratio = (*ratio + delta).clamp(0.1, 0.9);
981 }
982 Some(SplitNode::Leaf { .. }) => {
983 unreachable!("ContainerId {:?} points to a leaf", container_id)
984 }
985 None => {
986 unreachable!("ContainerId {:?} not found in split tree", container_id)
987 }
988 }
989 }
990
991 pub fn parent_container_of(&self, leaf_id: LeafId) -> Option<ContainerId> {
993 self.root.parent_container_of(leaf_id.into())
994 }
995
996 pub fn get_visible_buffers(&self, viewport_rect: Rect) -> Vec<(LeafId, BufferId, Rect)> {
998 if let Some(maximized_id) = self.maximized_split {
1000 if let Some(SplitNode::Leaf {
1001 buffer_id,
1002 split_id,
1003 }) = self.root.find(maximized_id)
1004 {
1005 return vec![(*split_id, *buffer_id, viewport_rect)];
1006 }
1007 }
1009 self.root.get_leaves_with_rects(viewport_rect)
1010 }
1011
1012 pub fn get_separators(&self, viewport_rect: Rect) -> Vec<(SplitDirection, u16, u16, u16)> {
1015 if self.maximized_split.is_some() {
1017 return vec![];
1018 }
1019 self.root.get_separators(viewport_rect)
1020 }
1021
1022 pub fn get_separators_with_ids(
1025 &self,
1026 viewport_rect: Rect,
1027 ) -> Vec<(ContainerId, SplitDirection, u16, u16, u16)> {
1028 if self.maximized_split.is_some() {
1030 return vec![];
1031 }
1032 self.root.get_separators_with_ids(viewport_rect)
1033 }
1034
1035 pub fn get_ratio(&self, split_id: SplitId) -> Option<f32> {
1037 if let Some(SplitNode::Split { ratio, .. }) = self.root.find(split_id) {
1038 Some(*ratio)
1039 } else {
1040 None
1041 }
1042 }
1043
1044 pub fn set_ratio(&mut self, container_id: ContainerId, new_ratio: f32) {
1046 match self.root.find_mut(container_id.into()) {
1047 Some(SplitNode::Split { ratio, .. }) => {
1048 *ratio = new_ratio.clamp(0.1, 0.9);
1049 }
1050 Some(SplitNode::Leaf { .. }) => {
1051 unreachable!("ContainerId {:?} points to a leaf", container_id)
1052 }
1053 None => {
1054 unreachable!("ContainerId {:?} not found in split tree", container_id)
1055 }
1056 }
1057 }
1058
1059 pub fn distribute_splits_evenly(&mut self) {
1062 Self::distribute_node_evenly(&mut self.root);
1063 }
1064
1065 fn distribute_node_evenly(node: &mut SplitNode) -> usize {
1068 match node {
1069 SplitNode::Leaf { .. } => 1,
1070 SplitNode::Split {
1071 first,
1072 second,
1073 ratio,
1074 ..
1075 } => {
1076 let first_leaves = Self::distribute_node_evenly(first);
1077 let second_leaves = Self::distribute_node_evenly(second);
1078 let total_leaves = first_leaves + second_leaves;
1079
1080 *ratio = (first_leaves as f32 / total_leaves as f32).clamp(0.1, 0.9);
1083
1084 total_leaves
1085 }
1086 }
1087 }
1088
1089 pub fn next_split(&mut self) {
1091 let leaf_ids = self.root.leaf_split_ids();
1092 if let Some(pos) = leaf_ids.iter().position(|id| *id == self.active_split) {
1093 let next_pos = (pos + 1) % leaf_ids.len();
1094 self.active_split = leaf_ids[next_pos];
1095 }
1096 }
1097
1098 pub fn prev_split(&mut self) {
1100 let leaf_ids = self.root.leaf_split_ids();
1101 if let Some(pos) = leaf_ids.iter().position(|id| *id == self.active_split) {
1102 let prev_pos = if pos == 0 { leaf_ids.len() } else { pos } - 1;
1103 self.active_split = leaf_ids[prev_pos];
1104 }
1105 }
1106
1107 pub fn splits_for_buffer(&self, target_buffer_id: BufferId) -> Vec<LeafId> {
1109 self.root
1110 .get_leaves_with_rects(Rect {
1111 x: 0,
1112 y: 0,
1113 width: 1,
1114 height: 1,
1115 })
1116 .into_iter()
1117 .filter(|(_, buffer_id, _)| *buffer_id == target_buffer_id)
1118 .map(|(split_id, _, _)| split_id)
1119 .collect()
1120 }
1121
1122 pub fn buffer_for_split(&self, target_split_id: LeafId) -> Option<BufferId> {
1124 self.root
1125 .get_leaves_with_rects(Rect {
1126 x: 0,
1127 y: 0,
1128 width: 1,
1129 height: 1,
1130 })
1131 .into_iter()
1132 .find(|(split_id, _, _)| *split_id == target_split_id)
1133 .map(|(_, buffer_id, _)| buffer_id)
1134 }
1135
1136 pub fn maximize_split(&mut self) -> Result<(), String> {
1139 if self.root.count_leaves() <= 1 {
1141 return Err("Cannot maximize: only one split exists".to_string());
1142 }
1143
1144 if self.maximized_split.is_some() {
1146 return Err("A split is already maximized".to_string());
1147 }
1148
1149 self.maximized_split = Some(self.active_split.into());
1151 Ok(())
1152 }
1153
1154 pub fn unmaximize_split(&mut self) -> Result<(), String> {
1157 if self.maximized_split.is_none() {
1158 return Err("No split is maximized".to_string());
1159 }
1160
1161 self.maximized_split = None;
1162 Ok(())
1163 }
1164
1165 pub fn is_maximized(&self) -> bool {
1167 self.maximized_split.is_some()
1168 }
1169
1170 pub fn maximized_split(&self) -> Option<SplitId> {
1172 self.maximized_split
1173 }
1174
1175 pub fn toggle_maximize(&mut self) -> Result<bool, String> {
1179 if self.is_maximized() {
1180 self.unmaximize_split()?;
1181 Ok(false)
1182 } else {
1183 self.maximize_split()?;
1184 Ok(true)
1185 }
1186 }
1187
1188 pub fn get_splits_in_group(
1190 &self,
1191 group_id: u32,
1192 view_states: &std::collections::HashMap<LeafId, SplitViewState>,
1193 ) -> Vec<LeafId> {
1194 self.root
1195 .leaf_split_ids()
1196 .into_iter()
1197 .filter(|id| {
1198 view_states
1199 .get(id)
1200 .and_then(|vs| vs.sync_group)
1201 .is_some_and(|g| g == group_id)
1202 })
1203 .collect()
1204 }
1205
1206 pub fn set_label(&mut self, split_id: LeafId, label: String) {
1210 self.labels.insert(split_id.into(), label);
1211 }
1212
1213 pub fn clear_label(&mut self, split_id: SplitId) {
1215 self.labels.remove(&split_id);
1216 }
1217
1218 pub fn get_label(&self, split_id: SplitId) -> Option<&str> {
1220 self.labels.get(&split_id).map(|s| s.as_str())
1221 }
1222
1223 pub fn labels(&self) -> &HashMap<SplitId, String> {
1225 &self.labels
1226 }
1227
1228 pub fn find_split_by_label(&self, label: &str) -> Option<LeafId> {
1230 self.root
1231 .leaf_split_ids()
1232 .into_iter()
1233 .find(|id| self.labels.get(&(*id).into()).is_some_and(|l| l == label))
1234 }
1235
1236 pub fn find_unlabeled_leaf(&self) -> Option<LeafId> {
1238 self.root
1239 .leaf_split_ids()
1240 .into_iter()
1241 .find(|id| !self.labels.contains_key(&(*id).into()))
1242 }
1243}
1244
1245#[cfg(test)]
1246mod tests {
1247 use super::*;
1248
1249 #[test]
1250 fn test_create_split_manager() {
1251 let buffer_id = BufferId(0);
1252 let manager = SplitManager::new(buffer_id);
1253
1254 assert_eq!(manager.active_buffer_id(), Some(buffer_id));
1255 assert_eq!(manager.root().count_leaves(), 1);
1256 }
1257
1258 #[test]
1259 fn test_horizontal_split() {
1260 let buffer_a = BufferId(0);
1261 let buffer_b = BufferId(1);
1262
1263 let mut manager = SplitManager::new(buffer_a);
1264 let result = manager.split_active(SplitDirection::Horizontal, buffer_b, 0.5);
1265
1266 assert!(result.is_ok());
1267 assert_eq!(manager.root().count_leaves(), 2);
1268 }
1269
1270 #[test]
1271 fn test_vertical_split() {
1272 let buffer_a = BufferId(0);
1273 let buffer_b = BufferId(1);
1274
1275 let mut manager = SplitManager::new(buffer_a);
1276 let result = manager.split_active(SplitDirection::Vertical, buffer_b, 0.5);
1277
1278 assert!(result.is_ok());
1279 assert_eq!(manager.root().count_leaves(), 2);
1280 }
1281
1282 #[test]
1283 fn test_nested_splits() {
1284 let buffer_a = BufferId(0);
1285 let buffer_b = BufferId(1);
1286 let buffer_c = BufferId(2);
1287
1288 let mut manager = SplitManager::new(buffer_a);
1289
1290 manager
1292 .split_active(SplitDirection::Horizontal, buffer_b, 0.5)
1293 .unwrap();
1294
1295 manager
1297 .split_active(SplitDirection::Vertical, buffer_c, 0.5)
1298 .unwrap();
1299
1300 assert_eq!(manager.root().count_leaves(), 3);
1301 }
1302
1303 #[test]
1304 fn test_close_split() {
1305 let buffer_a = BufferId(0);
1306 let buffer_b = BufferId(1);
1307
1308 let mut manager = SplitManager::new(buffer_a);
1309 let new_split = manager
1310 .split_active(SplitDirection::Horizontal, buffer_b, 0.5)
1311 .unwrap();
1312
1313 assert_eq!(manager.root().count_leaves(), 2);
1314
1315 let result = manager.close_split(new_split);
1317 assert!(result.is_ok());
1318 assert_eq!(manager.root().count_leaves(), 1);
1319 }
1320
1321 #[test]
1322 fn test_cannot_close_last_split() {
1323 let buffer_a = BufferId(0);
1324 let mut manager = SplitManager::new(buffer_a);
1325
1326 let result = manager.close_split(manager.active_split());
1327 assert!(result.is_err());
1328 }
1329
1330 #[test]
1331 fn test_split_rect_horizontal() {
1332 let rect = Rect {
1333 x: 0,
1334 y: 0,
1335 width: 100,
1336 height: 100,
1337 };
1338
1339 let (first, second) = split_rect(rect, SplitDirection::Horizontal, 0.5);
1340
1341 assert_eq!(first.height, 50);
1343 assert_eq!(second.height, 49);
1344 assert_eq!(first.width, 100);
1345 assert_eq!(second.width, 100);
1346 assert_eq!(first.y, 0);
1347 assert_eq!(second.y, 51); }
1349
1350 #[test]
1351 fn test_split_rect_vertical() {
1352 let rect = Rect {
1353 x: 0,
1354 y: 0,
1355 width: 100,
1356 height: 100,
1357 };
1358
1359 let (first, second) = split_rect(rect, SplitDirection::Vertical, 0.5);
1360
1361 assert_eq!(first.width, 50);
1363 assert_eq!(second.width, 49);
1364 assert_eq!(first.height, 100);
1365 assert_eq!(second.height, 100);
1366 assert_eq!(first.x, 0);
1367 assert_eq!(second.x, 51); }
1369
1370 #[test]
1373 fn test_set_and_get_label() {
1374 let mut manager = SplitManager::new(BufferId(0));
1375 let split = manager.active_split();
1376
1377 assert_eq!(manager.get_label(split.into()), None);
1378
1379 manager.set_label(split, "sidebar".to_string());
1380 assert_eq!(manager.get_label(split.into()), Some("sidebar"));
1381 }
1382
1383 #[test]
1384 fn test_clear_label() {
1385 let mut manager = SplitManager::new(BufferId(0));
1386 let split = manager.active_split();
1387
1388 manager.set_label(split, "sidebar".to_string());
1389 assert!(manager.get_label(split.into()).is_some());
1390
1391 manager.clear_label(split.into());
1392 assert_eq!(manager.get_label(split.into()), None);
1393 }
1394
1395 #[test]
1396 fn test_find_split_by_label() {
1397 let mut manager = SplitManager::new(BufferId(0));
1398 let first_split = manager.active_split();
1399
1400 let second_split = manager
1401 .split_active(SplitDirection::Vertical, BufferId(1), 0.5)
1402 .unwrap();
1403
1404 manager.set_label(first_split, "sidebar".to_string());
1405
1406 assert_eq!(manager.find_split_by_label("sidebar"), Some(first_split));
1407 assert_eq!(manager.find_split_by_label("terminal"), None);
1408
1409 assert_ne!(manager.find_split_by_label("sidebar"), Some(second_split));
1411 }
1412
1413 #[test]
1414 fn test_find_unlabeled_leaf() {
1415 let mut manager = SplitManager::new(BufferId(0));
1416 let first_split = manager.active_split();
1417
1418 let second_split = manager
1419 .split_active(SplitDirection::Vertical, BufferId(1), 0.5)
1420 .unwrap();
1421
1422 assert!(manager.find_unlabeled_leaf().is_some());
1424
1425 manager.set_label(first_split, "sidebar".to_string());
1427 assert_eq!(manager.find_unlabeled_leaf(), Some(second_split));
1428
1429 manager.set_label(second_split, "terminal".to_string());
1431 assert_eq!(manager.find_unlabeled_leaf(), None);
1432 }
1433
1434 #[test]
1435 fn test_close_split_cleans_up_label() {
1436 let mut manager = SplitManager::new(BufferId(0));
1437 let _first_split = manager.active_split();
1438
1439 let second_split = manager
1440 .split_active(SplitDirection::Vertical, BufferId(1), 0.5)
1441 .unwrap();
1442
1443 manager.set_label(second_split, "sidebar".to_string());
1444 assert_eq!(manager.find_split_by_label("sidebar"), Some(second_split));
1445
1446 manager.close_split(second_split).unwrap();
1447
1448 assert_eq!(manager.find_split_by_label("sidebar"), None);
1450 assert_eq!(manager.get_label(second_split.into()), None);
1451 }
1452
1453 #[test]
1454 fn test_label_overwrite() {
1455 let mut manager = SplitManager::new(BufferId(0));
1456 let split = manager.active_split();
1457
1458 manager.set_label(split, "sidebar".to_string());
1459 assert_eq!(manager.get_label(split.into()), Some("sidebar"));
1460
1461 manager.set_label(split, "terminal".to_string());
1462 assert_eq!(manager.get_label(split.into()), Some("terminal"));
1463 assert_eq!(manager.find_split_by_label("sidebar"), None);
1464 assert_eq!(manager.find_split_by_label("terminal"), Some(split));
1465 }
1466
1467 #[test]
1468 fn test_find_unlabeled_leaf_single_split_no_label() {
1469 let manager = SplitManager::new(BufferId(0));
1470 assert_eq!(manager.find_unlabeled_leaf(), Some(manager.active_split()));
1472 }
1473
1474 #[test]
1475 fn test_find_unlabeled_leaf_single_split_labeled() {
1476 let mut manager = SplitManager::new(BufferId(0));
1477 let split = manager.active_split();
1478 manager.set_label(split, "only".to_string());
1479 assert_eq!(manager.find_unlabeled_leaf(), None);
1481 }
1482}