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, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
43pub enum TabTarget {
44 Buffer(BufferId),
46 Group(LeafId),
48}
49
50impl TabTarget {
51 pub fn as_buffer(self) -> Option<BufferId> {
52 match self {
53 Self::Buffer(id) => Some(id),
54 Self::Group(_) => None,
55 }
56 }
57
58 pub fn as_group(self) -> Option<LeafId> {
59 match self {
60 Self::Buffer(_) => None,
61 Self::Group(id) => Some(id),
62 }
63 }
64}
65
66#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
68pub enum SplitNode {
69 Leaf {
71 buffer_id: BufferId,
73 split_id: LeafId,
75 },
76 Split {
78 direction: SplitDirection,
80 first: Box<Self>,
82 second: Box<Self>,
84 ratio: f32,
87 split_id: ContainerId,
89 #[serde(default)]
91 fixed_first: Option<u16>,
92 #[serde(default)]
94 fixed_second: Option<u16>,
95 },
96 Grouped {
101 split_id: LeafId,
104 name: String,
106 layout: Box<Self>,
108 active_inner_leaf: LeafId,
110 },
111}
112
113#[derive(Debug)]
120pub struct BufferViewState {
121 pub cursors: Cursors,
123
124 pub viewport: Viewport,
126
127 pub view_mode: ViewMode,
129
130 pub compose_width: Option<u16>,
132
133 pub compose_column_guides: Option<Vec<u16>>,
135
136 pub rulers: Vec<usize>,
138
139 pub show_line_numbers: bool,
144
145 pub highlight_current_line: bool,
149
150 pub view_transform: Option<ViewTransformPayload>,
152
153 pub view_transform_stale: bool,
157
158 pub plugin_state: std::collections::HashMap<String, serde_json::Value>,
162
163 pub folds: FoldManager,
165}
166
167impl BufferViewState {
168 pub fn ensure_cursor_visible(&mut self, buffer: &mut Buffer, marker_list: &MarkerList) {
174 let hidden: Vec<(usize, usize)> = self
175 .folds
176 .resolved_ranges(buffer, marker_list)
177 .into_iter()
178 .map(|r| (r.start_byte, r.end_byte))
179 .collect();
180 let cursor = *self.cursors.primary();
181 self.viewport.ensure_visible(buffer, &cursor, &hidden);
182 }
183
184 pub fn new(width: u16, height: u16) -> Self {
186 Self {
187 cursors: Cursors::new(),
188 viewport: Viewport::new(width, height),
189 view_mode: ViewMode::Source,
190 compose_width: None,
191 compose_column_guides: None,
192 rulers: Vec::new(),
193 show_line_numbers: true,
194 highlight_current_line: true,
195 view_transform: None,
196 view_transform_stale: false,
197 plugin_state: std::collections::HashMap::new(),
198 folds: FoldManager::new(),
199 }
200 }
201
202 pub fn apply_config_defaults(
209 &mut self,
210 line_numbers: bool,
211 highlight_current_line: bool,
212 line_wrap: bool,
213 wrap_indent: bool,
214 wrap_column: Option<usize>,
215 rulers: Vec<usize>,
216 ) {
217 self.show_line_numbers = line_numbers;
218 self.highlight_current_line = highlight_current_line;
219 self.viewport.line_wrap_enabled = line_wrap;
220 self.viewport.wrap_indent = wrap_indent;
221 self.viewport.wrap_column = wrap_column;
222 self.rulers = rulers;
223 }
224
225 pub fn activate_page_view(&mut self, page_width: Option<usize>) {
231 self.view_mode = ViewMode::PageView;
232 self.show_line_numbers = false;
233 self.viewport.line_wrap_enabled = false;
234 if let Some(width) = page_width {
235 self.compose_width = Some(width as u16);
236 }
237 }
238}
239
240impl Clone for BufferViewState {
241 fn clone(&self) -> Self {
242 Self {
243 cursors: self.cursors.clone(),
244 viewport: self.viewport.clone(),
245 view_mode: self.view_mode.clone(),
246 compose_width: self.compose_width,
247 compose_column_guides: self.compose_column_guides.clone(),
248 rulers: self.rulers.clone(),
249 show_line_numbers: self.show_line_numbers,
250 highlight_current_line: self.highlight_current_line,
251 view_transform: self.view_transform.clone(),
252 view_transform_stale: self.view_transform_stale,
253 plugin_state: self.plugin_state.clone(),
254 folds: FoldManager::new(),
256 }
257 }
258}
259
260#[derive(Debug, Clone)]
272pub struct SplitViewState {
273 pub active_buffer: BufferId,
275
276 pub keyed_states: HashMap<BufferId, BufferViewState>,
278
279 pub open_buffers: Vec<TabTarget>,
285
286 pub tab_scroll_offset: usize,
288
289 pub layout: Option<Layout>,
292
293 pub layout_dirty: bool,
295
296 pub focus_history: Vec<TabTarget>,
300
301 pub sync_group: Option<u32>,
304
305 pub composite_view: Option<BufferId>,
310
311 pub suppress_chrome: bool,
314
315 pub hide_tilde: bool,
318
319 pub active_group_tab: Option<LeafId>,
323
324 pub focused_group_leaf: Option<LeafId>,
327}
328
329impl std::ops::Deref for SplitViewState {
330 type Target = BufferViewState;
331
332 fn deref(&self) -> &BufferViewState {
333 self.active_state()
334 }
335}
336
337impl std::ops::DerefMut for SplitViewState {
338 fn deref_mut(&mut self) -> &mut BufferViewState {
339 self.active_state_mut()
340 }
341}
342
343impl SplitViewState {
344 pub fn with_buffer(width: u16, height: u16, buffer_id: BufferId) -> Self {
346 let buf_state = BufferViewState::new(width, height);
347 let mut keyed_states = HashMap::new();
348 keyed_states.insert(buffer_id, buf_state);
349 Self {
350 active_buffer: buffer_id,
351 keyed_states,
352 open_buffers: vec![TabTarget::Buffer(buffer_id)],
353 tab_scroll_offset: 0,
354 layout: None,
355 layout_dirty: true,
356 focus_history: Vec::new(),
357 sync_group: None,
358 composite_view: None,
359 suppress_chrome: false,
360 hide_tilde: false,
361 active_group_tab: None,
362 focused_group_leaf: None,
363 }
364 }
365
366 pub fn active_state(&self) -> &BufferViewState {
368 self.keyed_states
369 .get(&self.active_buffer)
370 .expect("active_buffer must always have an entry in keyed_states")
371 }
372
373 pub fn active_state_mut(&mut self) -> &mut BufferViewState {
375 self.keyed_states
376 .get_mut(&self.active_buffer)
377 .expect("active_buffer must always have an entry in keyed_states")
378 }
379
380 pub fn switch_buffer(&mut self, new_buffer_id: BufferId) {
386 if new_buffer_id == self.active_buffer {
387 return;
388 }
389 if !self.keyed_states.contains_key(&new_buffer_id) {
391 let active = self.active_state();
392 let width = active.viewport.width;
393 let height = active.viewport.height;
394 self.keyed_states
395 .insert(new_buffer_id, BufferViewState::new(width, height));
396 }
397 self.active_buffer = new_buffer_id;
398 self.layout_dirty = true;
400 }
401
402 pub fn buffer_state(&self, buffer_id: BufferId) -> Option<&BufferViewState> {
404 self.keyed_states.get(&buffer_id)
405 }
406
407 pub fn buffer_state_mut(&mut self, buffer_id: BufferId) -> Option<&mut BufferViewState> {
409 self.keyed_states.get_mut(&buffer_id)
410 }
411
412 pub fn ensure_buffer_state(&mut self, buffer_id: BufferId) -> &mut BufferViewState {
415 let (width, height) = {
416 let active = self.active_state();
417 (active.viewport.width, active.viewport.height)
418 };
419 self.keyed_states
420 .entry(buffer_id)
421 .or_insert_with(|| BufferViewState::new(width, height))
422 }
423
424 pub fn remove_buffer_state(&mut self, buffer_id: BufferId) {
426 if buffer_id != self.active_buffer {
427 self.keyed_states.remove(&buffer_id);
428 }
429 }
430
431 pub fn invalidate_layout(&mut self) {
433 self.layout_dirty = true;
434 }
435
436 pub fn ensure_layout(
444 &mut self,
445 tokens: &[fresh_core::api::ViewTokenWire],
446 source_range: std::ops::Range<usize>,
447 tab_size: usize,
448 ) -> &Layout {
449 if self.layout.is_none() || self.layout_dirty {
450 self.layout = Some(Layout::from_tokens(tokens, source_range, tab_size));
451 self.layout_dirty = false;
452 }
453 self.layout.as_ref().unwrap()
454 }
455
456 pub fn get_layout(&self) -> Option<&Layout> {
458 if self.layout_dirty {
459 None
460 } else {
461 self.layout.as_ref()
462 }
463 }
464
465 pub fn add_buffer(&mut self, buffer_id: BufferId) {
467 if !self.has_buffer(buffer_id) {
468 self.open_buffers.push(TabTarget::Buffer(buffer_id));
469 }
470 }
471
472 pub fn remove_buffer(&mut self, buffer_id: BufferId) {
474 self.open_buffers
475 .retain(|t| *t != TabTarget::Buffer(buffer_id));
476 if buffer_id != self.active_buffer {
478 self.keyed_states.remove(&buffer_id);
479 }
480 }
481
482 pub fn has_buffer(&self, buffer_id: BufferId) -> bool {
484 self.open_buffers.contains(&TabTarget::Buffer(buffer_id))
485 }
486
487 pub fn add_group(&mut self, leaf_id: LeafId) {
489 if !self.has_group(leaf_id) {
490 self.open_buffers.push(TabTarget::Group(leaf_id));
491 }
492 }
493
494 pub fn remove_group(&mut self, leaf_id: LeafId) {
496 self.open_buffers
497 .retain(|t| *t != TabTarget::Group(leaf_id));
498 }
499
500 pub fn has_group(&self, leaf_id: LeafId) -> bool {
502 self.open_buffers.contains(&TabTarget::Group(leaf_id))
503 }
504
505 pub fn buffer_tab_ids(&self) -> impl Iterator<Item = BufferId> + '_ {
507 self.open_buffers.iter().filter_map(|t| t.as_buffer())
508 }
509
510 pub fn buffer_tab_ids_vec(&self) -> Vec<BufferId> {
513 self.buffer_tab_ids().collect()
514 }
515
516 pub fn buffer_tab_count(&self) -> usize {
518 self.open_buffers
519 .iter()
520 .filter(|t| matches!(t, TabTarget::Buffer(_)))
521 .count()
522 }
523
524 pub fn active_target(&self) -> TabTarget {
528 match self.active_group_tab {
529 Some(leaf_id) => TabTarget::Group(leaf_id),
530 None => TabTarget::Buffer(self.active_buffer),
531 }
532 }
533
534 pub fn set_active_buffer_tab(&mut self, buffer_id: BufferId) {
537 self.active_group_tab = None;
538 self.focused_group_leaf = None;
539 self.switch_buffer(buffer_id);
540 }
541
542 pub fn set_active_group_tab(&mut self, leaf_id: LeafId) {
544 self.active_group_tab = Some(leaf_id);
545 }
546
547 pub fn push_focus(&mut self, target: TabTarget) {
550 self.focus_history.retain(|t| *t != target);
551 self.focus_history.push(target);
552 if self.focus_history.len() > 50 {
553 self.focus_history.remove(0);
554 }
555 }
556
557 pub fn previous_tab(&self) -> Option<TabTarget> {
559 self.focus_history.last().copied()
560 }
561
562 pub fn remove_from_history(&mut self, buffer_id: BufferId) {
564 self.focus_history
565 .retain(|t| *t != TabTarget::Buffer(buffer_id));
566 }
567
568 pub fn remove_group_from_history(&mut self, leaf_id: LeafId) {
570 self.focus_history
571 .retain(|t| *t != TabTarget::Group(leaf_id));
572 }
573}
574
575impl SplitNode {
576 pub fn leaf(buffer_id: BufferId, split_id: SplitId) -> Self {
578 Self::Leaf {
579 buffer_id,
580 split_id: LeafId(split_id),
581 }
582 }
583
584 pub fn split(
586 direction: SplitDirection,
587 first: SplitNode,
588 second: SplitNode,
589 ratio: f32,
590 split_id: SplitId,
591 ) -> Self {
592 SplitNode::Split {
593 direction,
594 first: Box::new(first),
595 second: Box::new(second),
596 ratio: ratio.clamp(0.1, 0.9), split_id: ContainerId(split_id),
598 fixed_first: None,
599 fixed_second: None,
600 }
601 }
602
603 pub fn id(&self) -> SplitId {
605 match self {
606 Self::Leaf { split_id, .. } => split_id.0,
607 Self::Split { split_id, .. } => split_id.0,
608 Self::Grouped { split_id, .. } => split_id.0,
609 }
610 }
611
612 pub fn buffer_id(&self) -> Option<BufferId> {
614 match self {
615 Self::Leaf { buffer_id, .. } => Some(*buffer_id),
616 Self::Split { .. } | Self::Grouped { .. } => None,
617 }
618 }
619
620 pub fn find_mut(&mut self, target_id: SplitId) -> Option<&mut Self> {
624 if self.id() == target_id {
625 return Some(self);
626 }
627
628 match self {
629 Self::Leaf { .. } => None,
630 Self::Split { first, second, .. } => first
631 .find_mut(target_id)
632 .or_else(|| second.find_mut(target_id)),
633 Self::Grouped { layout, .. } => layout.find_mut(target_id),
634 }
635 }
636
637 pub fn find(&self, target_id: SplitId) -> Option<&Self> {
641 if self.id() == target_id {
642 return Some(self);
643 }
644
645 match self {
646 Self::Leaf { .. } => None,
647 Self::Split { first, second, .. } => {
648 first.find(target_id).or_else(|| second.find(target_id))
649 }
650 Self::Grouped { layout, .. } => layout.find(target_id),
651 }
652 }
653
654 pub fn parent_container_of(&self, target_id: SplitId) -> Option<ContainerId> {
658 match self {
659 Self::Leaf { .. } => None,
660 Self::Split {
661 split_id,
662 first,
663 second,
664 ..
665 } => {
666 if first.id() == target_id || second.id() == target_id {
667 Some(*split_id)
668 } else {
669 first
670 .parent_container_of(target_id)
671 .or_else(|| second.parent_container_of(target_id))
672 }
673 }
674 Self::Grouped { layout, .. } => layout.parent_container_of(target_id),
675 }
676 }
677
678 pub fn grouped_ancestor_of(&self, target_id: SplitId) -> Option<LeafId> {
681 match self {
682 Self::Leaf { .. } => None,
683 Self::Split { first, second, .. } => first
684 .grouped_ancestor_of(target_id)
685 .or_else(|| second.grouped_ancestor_of(target_id)),
686 Self::Grouped {
687 split_id, layout, ..
688 } => {
689 if layout.find(target_id).is_some() {
690 Some(*split_id)
691 } else {
692 layout.grouped_ancestor_of(target_id)
693 }
694 }
695 }
696 }
697
698 pub fn find_grouped(&self, target: LeafId) -> Option<&Self> {
701 match self {
702 Self::Leaf { .. } => None,
703 Self::Split { first, second, .. } => first
704 .find_grouped(target)
705 .or_else(|| second.find_grouped(target)),
706 Self::Grouped {
707 split_id, layout, ..
708 } => {
709 if *split_id == target {
710 Some(self)
711 } else {
712 layout.find_grouped(target)
713 }
714 }
715 }
716 }
717
718 pub fn get_leaves_with_rects(&self, rect: Rect) -> Vec<(LeafId, BufferId, Rect)> {
724 match self {
725 Self::Leaf {
726 buffer_id,
727 split_id,
728 } => {
729 vec![(*split_id, *buffer_id, rect)]
730 }
731 Self::Split {
732 direction,
733 first,
734 second,
735 ratio,
736 fixed_first,
737 fixed_second,
738 ..
739 } => {
740 let (first_rect, second_rect) =
741 split_rect_ext(rect, *direction, *ratio, *fixed_first, *fixed_second);
742 let mut leaves = first.get_leaves_with_rects(first_rect);
743 leaves.extend(second.get_leaves_with_rects(second_rect));
744 leaves
745 }
746 Self::Grouped { layout, .. } => layout.get_leaves_with_rects(rect),
747 }
748 }
749
750 pub fn get_visible_leaves_with_rects<F>(
756 &self,
757 rect: Rect,
758 is_group_active: &F,
759 ) -> Vec<(LeafId, BufferId, Rect)>
760 where
761 F: Fn(LeafId) -> bool,
762 {
763 match self {
764 Self::Leaf {
765 buffer_id,
766 split_id,
767 } => {
768 vec![(*split_id, *buffer_id, rect)]
769 }
770 Self::Split {
771 direction,
772 first,
773 second,
774 ratio,
775 fixed_first,
776 fixed_second,
777 ..
778 } => {
779 let (first_rect, second_rect) =
780 split_rect_ext(rect, *direction, *ratio, *fixed_first, *fixed_second);
781 let mut leaves = first.get_visible_leaves_with_rects(first_rect, is_group_active);
782 leaves.extend(second.get_visible_leaves_with_rects(second_rect, is_group_active));
783 leaves
784 }
785 Self::Grouped {
786 split_id, layout, ..
787 } => {
788 if is_group_active(*split_id) {
789 layout.get_visible_leaves_with_rects(rect, is_group_active)
790 } else {
791 Vec::new()
792 }
793 }
794 }
795 }
796
797 pub fn get_separators(&self, rect: Rect) -> Vec<(SplitDirection, u16, u16, u16)> {
800 self.get_separators_with_ids(rect)
801 .into_iter()
802 .map(|(_, dir, x, y, len)| (dir, x, y, len))
803 .collect()
804 }
805
806 pub fn get_separators_with_ids(
809 &self,
810 rect: Rect,
811 ) -> Vec<(ContainerId, SplitDirection, u16, u16, u16)> {
812 match self {
813 Self::Leaf { .. } => vec![],
814 Self::Grouped { layout, .. } => layout.get_separators_with_ids(rect),
815 Self::Split {
816 direction,
817 first,
818 second,
819 ratio,
820 split_id,
821 fixed_first,
822 fixed_second,
823 } => {
824 let (first_rect, second_rect) =
825 split_rect_ext(rect, *direction, *ratio, *fixed_first, *fixed_second);
826 let mut separators = Vec::new();
827
828 match direction {
830 SplitDirection::Horizontal => {
831 separators.push((
834 *split_id,
835 SplitDirection::Horizontal,
836 rect.x,
837 first_rect.y + first_rect.height,
838 rect.width,
839 ));
840 }
841 SplitDirection::Vertical => {
842 separators.push((
845 *split_id,
846 SplitDirection::Vertical,
847 first_rect.x + first_rect.width,
848 rect.y,
849 rect.height,
850 ));
851 }
852 }
853
854 separators.extend(first.get_separators_with_ids(first_rect));
856 separators.extend(second.get_separators_with_ids(second_rect));
857 separators
858 }
859 }
860 }
861
862 pub fn all_split_ids(&self) -> Vec<SplitId> {
864 let mut ids = vec![self.id()];
865 match self {
866 Self::Leaf { .. } => ids,
867 Self::Split { first, second, .. } => {
868 ids.extend(first.all_split_ids());
869 ids.extend(second.all_split_ids());
870 ids
871 }
872 Self::Grouped { layout, .. } => {
873 ids.extend(layout.all_split_ids());
874 ids
875 }
876 }
877 }
878
879 pub fn leaf_split_ids(&self) -> Vec<LeafId> {
882 match self {
883 Self::Leaf { split_id, .. } => vec![*split_id],
884 Self::Split { first, second, .. } => {
885 let mut ids = first.leaf_split_ids();
886 ids.extend(second.leaf_split_ids());
887 ids
888 }
889 Self::Grouped { layout, .. } => layout.leaf_split_ids(),
890 }
891 }
892
893 pub fn count_leaves(&self) -> usize {
896 match self {
897 Self::Leaf { .. } => 1,
898 Self::Split { first, second, .. } => first.count_leaves() + second.count_leaves(),
899 Self::Grouped { layout, .. } => layout.count_leaves(),
900 }
901 }
902
903 pub fn collect_group_names(&self) -> HashMap<LeafId, String> {
906 let mut map = HashMap::new();
907 self.collect_group_names_into(&mut map);
908 map
909 }
910
911 fn collect_group_names_into(&self, map: &mut HashMap<LeafId, String>) {
912 match self {
913 Self::Leaf { .. } => {}
914 Self::Split { first, second, .. } => {
915 first.collect_group_names_into(map);
916 second.collect_group_names_into(map);
917 }
918 Self::Grouped {
919 split_id,
920 name,
921 layout,
922 ..
923 } => {
924 map.insert(*split_id, name.clone());
925 layout.collect_group_names_into(map);
926 }
927 }
928 }
929}
930
931#[cfg(test)]
934fn split_rect(rect: Rect, direction: SplitDirection, ratio: f32) -> (Rect, Rect) {
935 split_rect_ext(rect, direction, ratio, None, None)
936}
937
938fn split_rect_ext(
939 rect: Rect,
940 direction: SplitDirection,
941 ratio: f32,
942 fixed_first: Option<u16>,
943 fixed_second: Option<u16>,
944) -> (Rect, Rect) {
945 match direction {
946 SplitDirection::Horizontal => {
947 let total_height = rect.height.saturating_sub(1); let first_height = if let Some(f) = fixed_first {
950 f.min(total_height)
951 } else if let Some(s) = fixed_second {
952 total_height.saturating_sub(s.min(total_height))
953 } else {
954 (total_height as f32 * ratio).round() as u16
955 };
956 let second_height = total_height.saturating_sub(first_height);
957
958 let first = Rect {
959 x: rect.x,
960 y: rect.y,
961 width: rect.width,
962 height: first_height,
963 };
964
965 let second = Rect {
966 x: rect.x,
967 y: rect.y + first_height + 1, width: rect.width,
969 height: second_height,
970 };
971
972 (first, second)
973 }
974 SplitDirection::Vertical => {
975 let total_width = rect.width.saturating_sub(1); let first_width = if let Some(f) = fixed_first {
978 f.min(total_width)
979 } else if let Some(s) = fixed_second {
980 total_width.saturating_sub(s.min(total_width))
981 } else {
982 (total_width as f32 * ratio).round() as u16
983 };
984 let second_width = total_width.saturating_sub(first_width);
985
986 let first = Rect {
987 x: rect.x,
988 y: rect.y,
989 width: first_width,
990 height: rect.height,
991 };
992
993 let second = Rect {
994 x: rect.x + first_width + 1, y: rect.y,
996 width: second_width,
997 height: rect.height,
998 };
999
1000 (first, second)
1001 }
1002 }
1003}
1004
1005#[derive(Debug)]
1007pub struct SplitManager {
1008 root: SplitNode,
1010
1011 active_split: LeafId,
1013
1014 next_split_id: usize,
1016
1017 maximized_split: Option<SplitId>,
1019
1020 labels: HashMap<SplitId, String>,
1022}
1023
1024impl SplitManager {
1025 pub fn new(buffer_id: BufferId) -> Self {
1027 let split_id = SplitId(0);
1028 Self {
1029 root: SplitNode::leaf(buffer_id, split_id),
1030 active_split: LeafId(split_id),
1031 next_split_id: 1,
1032 maximized_split: None,
1033 labels: HashMap::new(),
1034 }
1035 }
1036
1037 pub fn root(&self) -> &SplitNode {
1039 &self.root
1040 }
1041
1042 pub fn allocate_split_id(&mut self) -> SplitId {
1044 let id = SplitId(self.next_split_id);
1045 self.next_split_id += 1;
1046 id
1047 }
1048
1049 pub fn replace_root(&mut self, new_root: SplitNode, new_active: LeafId) {
1053 self.root = new_root;
1054 self.active_split = new_active;
1055 }
1056
1057 pub fn active_split(&self) -> LeafId {
1059 self.active_split
1060 }
1061
1062 pub fn set_active_split(&mut self, split_id: LeafId) -> bool {
1064 if self.root.find(split_id.into()).is_some() {
1066 self.active_split = split_id;
1067 true
1068 } else {
1069 false
1070 }
1071 }
1072
1073 pub fn active_buffer_id(&self) -> Option<BufferId> {
1075 self.root
1076 .find(self.active_split.into())
1077 .and_then(|node| node.buffer_id())
1078 }
1079
1080 pub fn get_buffer_id(&self, split_id: SplitId) -> Option<BufferId> {
1082 self.root.find(split_id).and_then(|node| node.buffer_id())
1083 }
1084
1085 pub fn set_active_buffer_id(&mut self, new_buffer_id: BufferId) -> bool {
1087 if let Some(SplitNode::Leaf { buffer_id, .. }) =
1088 self.root.find_mut(self.active_split.into())
1089 {
1090 *buffer_id = new_buffer_id;
1091 return true;
1092 }
1093 false
1094 }
1095
1096 pub fn set_split_buffer(&mut self, leaf_id: LeafId, new_buffer_id: BufferId) {
1098 match self.root.find_mut(leaf_id.into()) {
1099 Some(SplitNode::Leaf { buffer_id, .. }) => {
1100 *buffer_id = new_buffer_id;
1101 }
1102 Some(SplitNode::Split { .. }) => {
1103 unreachable!("LeafId {:?} points to a container", leaf_id)
1104 }
1105 Some(SplitNode::Grouped { .. }) => {
1106 unreachable!("LeafId {:?} points to a Grouped node", leaf_id)
1107 }
1108 None => {
1109 unreachable!("LeafId {:?} not found in split tree", leaf_id)
1110 }
1111 }
1112 }
1113
1114 pub fn split_active(
1118 &mut self,
1119 direction: SplitDirection,
1120 new_buffer_id: BufferId,
1121 ratio: f32,
1122 ) -> Result<LeafId, String> {
1123 self.split_active_positioned(direction, new_buffer_id, ratio, false)
1124 }
1125
1126 pub fn split_active_before(
1129 &mut self,
1130 direction: SplitDirection,
1131 new_buffer_id: BufferId,
1132 ratio: f32,
1133 ) -> Result<LeafId, String> {
1134 self.split_active_positioned(direction, new_buffer_id, ratio, true)
1135 }
1136
1137 pub fn split_active_positioned(
1138 &mut self,
1139 direction: SplitDirection,
1140 new_buffer_id: BufferId,
1141 ratio: f32,
1142 before: bool,
1143 ) -> Result<LeafId, String> {
1144 let active_id: SplitId = self.active_split.into();
1145
1146 let result =
1148 self.replace_split_with_split(active_id, direction, new_buffer_id, ratio, before);
1149
1150 if let Ok(new_split_id) = &result {
1151 self.active_split = *new_split_id;
1153 }
1154 result
1155 }
1156
1157 fn replace_split_with_split(
1160 &mut self,
1161 target_id: SplitId,
1162 direction: SplitDirection,
1163 new_buffer_id: BufferId,
1164 ratio: f32,
1165 before: bool,
1166 ) -> Result<LeafId, String> {
1167 let temp_id = self.allocate_split_id();
1169 let new_split_id = self.allocate_split_id();
1170 let new_leaf_id = self.allocate_split_id();
1171
1172 if self.root.id() == target_id {
1174 let old_root =
1175 std::mem::replace(&mut self.root, SplitNode::leaf(new_buffer_id, temp_id));
1176 let new_leaf = SplitNode::leaf(new_buffer_id, new_leaf_id);
1177
1178 let (first, second) = if before {
1179 (new_leaf, old_root)
1180 } else {
1181 (old_root, new_leaf)
1182 };
1183
1184 self.root = SplitNode::split(direction, first, second, ratio, new_split_id);
1185
1186 return Ok(LeafId(new_leaf_id));
1187 }
1188
1189 if let Some(node) = self.root.find_mut(target_id) {
1191 let old_node = std::mem::replace(node, SplitNode::leaf(new_buffer_id, temp_id));
1192 let new_leaf = SplitNode::leaf(new_buffer_id, new_leaf_id);
1193
1194 let (first, second) = if before {
1195 (new_leaf, old_node)
1196 } else {
1197 (old_node, new_leaf)
1198 };
1199
1200 *node = SplitNode::split(direction, first, second, ratio, new_split_id);
1201
1202 Ok(LeafId(new_leaf_id))
1203 } else {
1204 Err(format!("Split {:?} not found", target_id))
1205 }
1206 }
1207
1208 pub fn close_split(&mut self, split_id: LeafId) -> Result<(), String> {
1210 if self.root.count_leaves() <= 1 {
1212 return Err("Cannot close the last split".to_string());
1213 }
1214
1215 if self.root.id() == split_id.into() && self.root.buffer_id().is_some() {
1217 return Err("Cannot close the only split".to_string());
1218 }
1219
1220 if self.maximized_split == Some(split_id.into()) {
1222 self.maximized_split = None;
1223 }
1224
1225 let removed_ids: Vec<SplitId> = self
1227 .root
1228 .find(split_id.into())
1229 .map(|node| node.all_split_ids())
1230 .unwrap_or_default();
1231
1232 let result = self.remove_split_node(split_id.into());
1235
1236 if result.is_ok() {
1237 for id in &removed_ids {
1239 self.labels.remove(id);
1240 }
1241
1242 if self.active_split == split_id {
1244 let leaf_ids = self.root.leaf_split_ids();
1245 if let Some(&first_leaf) = leaf_ids.first() {
1246 self.active_split = first_leaf;
1247 }
1248 }
1249 }
1250
1251 result
1252 }
1253
1254 fn remove_split_node(&mut self, target_id: SplitId) -> Result<(), String> {
1256 if self.root.id() == target_id {
1258 if let SplitNode::Split { first, .. } = &self.root {
1259 self.root = (**first).clone();
1262 return Ok(());
1263 }
1264 }
1265
1266 Self::remove_child_static(&mut self.root, target_id)
1268 }
1269
1270 fn remove_child_static(node: &mut SplitNode, target_id: SplitId) -> Result<(), String> {
1272 match node {
1273 SplitNode::Leaf { .. } => Err("Target not found".to_string()),
1274 SplitNode::Grouped { layout, .. } => Self::remove_child_static(layout, target_id),
1275 SplitNode::Split { first, second, .. } => {
1276 if first.id() == target_id {
1278 *node = (**second).clone();
1280 Ok(())
1281 } else if second.id() == target_id {
1282 *node = (**first).clone();
1284 Ok(())
1285 } else {
1286 Self::remove_child_static(first, target_id)
1288 .or_else(|_| Self::remove_child_static(second, target_id))
1289 }
1290 }
1291 }
1292 }
1293
1294 pub fn remove_grouped(&mut self, target: LeafId) -> Result<(), String> {
1302 let target_id: SplitId = target.into();
1303 if self.root.id() == target_id {
1304 return Err("Cannot remove root Grouped node".to_string());
1305 }
1306 Self::remove_child_static(&mut self.root, target_id)
1307 }
1308
1309 pub fn adjust_ratio(&mut self, container_id: ContainerId, delta: f32) {
1311 match self.root.find_mut(container_id.into()) {
1312 Some(SplitNode::Split { ratio, .. }) => {
1313 *ratio = (*ratio + delta).clamp(0.1, 0.9);
1314 }
1315 Some(SplitNode::Leaf { .. }) => {
1316 unreachable!("ContainerId {:?} points to a leaf", container_id)
1317 }
1318 Some(SplitNode::Grouped { .. }) => {
1319 unreachable!("ContainerId {:?} points to a Grouped node", container_id)
1320 }
1321 None => {
1322 unreachable!("ContainerId {:?} not found in split tree", container_id)
1323 }
1324 }
1325 }
1326
1327 pub fn parent_container_of(&self, leaf_id: LeafId) -> Option<ContainerId> {
1329 self.root.parent_container_of(leaf_id.into())
1330 }
1331
1332 pub fn get_visible_buffers(&self, viewport_rect: Rect) -> Vec<(LeafId, BufferId, Rect)> {
1334 if let Some(maximized_id) = self.maximized_split {
1336 if let Some(SplitNode::Leaf {
1337 buffer_id,
1338 split_id,
1339 }) = self.root.find(maximized_id)
1340 {
1341 return vec![(*split_id, *buffer_id, viewport_rect)];
1342 }
1343 }
1345 self.root.get_leaves_with_rects(viewport_rect)
1346 }
1347
1348 pub fn get_separators(&self, viewport_rect: Rect) -> Vec<(SplitDirection, u16, u16, u16)> {
1351 if self.maximized_split.is_some() {
1353 return vec![];
1354 }
1355 self.root.get_separators(viewport_rect)
1356 }
1357
1358 pub fn get_separators_with_ids(
1361 &self,
1362 viewport_rect: Rect,
1363 ) -> Vec<(ContainerId, SplitDirection, u16, u16, u16)> {
1364 if self.maximized_split.is_some() {
1366 return vec![];
1367 }
1368 self.root.get_separators_with_ids(viewport_rect)
1369 }
1370
1371 pub fn get_ratio(&self, split_id: SplitId) -> Option<f32> {
1373 if let Some(SplitNode::Split { ratio, .. }) = self.root.find(split_id) {
1374 Some(*ratio)
1375 } else {
1376 None
1377 }
1378 }
1379
1380 pub fn set_ratio(&mut self, container_id: ContainerId, new_ratio: f32) {
1382 match self.root.find_mut(container_id.into()) {
1383 Some(SplitNode::Split { ratio, .. }) => {
1384 *ratio = new_ratio.clamp(0.1, 0.9);
1385 }
1386 Some(SplitNode::Leaf { .. }) => {
1387 unreachable!("ContainerId {:?} points to a leaf", container_id)
1388 }
1389 Some(SplitNode::Grouped { .. }) => {
1390 unreachable!("ContainerId {:?} points to a Grouped node", container_id)
1391 }
1392 None => {
1393 unreachable!("ContainerId {:?} not found in split tree", container_id)
1394 }
1395 }
1396 }
1397
1398 pub fn set_fixed_size(
1401 &mut self,
1402 container_id: ContainerId,
1403 first: Option<u16>,
1404 second: Option<u16>,
1405 ) {
1406 match self.root.find_mut(container_id.into()) {
1407 Some(SplitNode::Split {
1408 fixed_first,
1409 fixed_second,
1410 ..
1411 }) => {
1412 *fixed_first = first;
1413 *fixed_second = second;
1414 }
1415 _ => {}
1416 }
1417 }
1418
1419 pub fn distribute_splits_evenly(&mut self) {
1422 Self::distribute_node_evenly(&mut self.root);
1423 }
1424
1425 fn distribute_node_evenly(node: &mut SplitNode) -> usize {
1428 match node {
1429 SplitNode::Leaf { .. } => 1,
1430 SplitNode::Grouped { layout, .. } => Self::distribute_node_evenly(layout),
1431 SplitNode::Split {
1432 first,
1433 second,
1434 ratio,
1435 ..
1436 } => {
1437 let first_leaves = Self::distribute_node_evenly(first);
1438 let second_leaves = Self::distribute_node_evenly(second);
1439 let total_leaves = first_leaves + second_leaves;
1440
1441 *ratio = (first_leaves as f32 / total_leaves as f32).clamp(0.1, 0.9);
1444
1445 total_leaves
1446 }
1447 }
1448 }
1449
1450 pub fn next_split(&mut self) {
1452 let leaf_ids = self.root.leaf_split_ids();
1453 if let Some(pos) = leaf_ids.iter().position(|id| *id == self.active_split) {
1454 let next_pos = (pos + 1) % leaf_ids.len();
1455 self.active_split = leaf_ids[next_pos];
1456 }
1457 }
1458
1459 pub fn prev_split(&mut self) {
1461 let leaf_ids = self.root.leaf_split_ids();
1462 if let Some(pos) = leaf_ids.iter().position(|id| *id == self.active_split) {
1463 let prev_pos = if pos == 0 { leaf_ids.len() } else { pos } - 1;
1464 self.active_split = leaf_ids[prev_pos];
1465 }
1466 }
1467
1468 pub fn splits_for_buffer(&self, target_buffer_id: BufferId) -> Vec<LeafId> {
1470 self.root
1471 .get_leaves_with_rects(Rect {
1472 x: 0,
1473 y: 0,
1474 width: 1,
1475 height: 1,
1476 })
1477 .into_iter()
1478 .filter(|(_, buffer_id, _)| *buffer_id == target_buffer_id)
1479 .map(|(split_id, _, _)| split_id)
1480 .collect()
1481 }
1482
1483 pub fn buffer_for_split(&self, target_split_id: LeafId) -> Option<BufferId> {
1485 self.root
1486 .get_leaves_with_rects(Rect {
1487 x: 0,
1488 y: 0,
1489 width: 1,
1490 height: 1,
1491 })
1492 .into_iter()
1493 .find(|(split_id, _, _)| *split_id == target_split_id)
1494 .map(|(_, buffer_id, _)| buffer_id)
1495 }
1496
1497 pub fn maximize_split(&mut self) -> Result<(), String> {
1500 if self.root.count_leaves() <= 1 {
1502 return Err("Cannot maximize: only one split exists".to_string());
1503 }
1504
1505 if self.maximized_split.is_some() {
1507 return Err("A split is already maximized".to_string());
1508 }
1509
1510 self.maximized_split = Some(self.active_split.into());
1512 Ok(())
1513 }
1514
1515 pub fn unmaximize_split(&mut self) -> Result<(), String> {
1518 if self.maximized_split.is_none() {
1519 return Err("No split is maximized".to_string());
1520 }
1521
1522 self.maximized_split = None;
1523 Ok(())
1524 }
1525
1526 pub fn is_maximized(&self) -> bool {
1528 self.maximized_split.is_some()
1529 }
1530
1531 pub fn maximized_split(&self) -> Option<SplitId> {
1533 self.maximized_split
1534 }
1535
1536 pub fn toggle_maximize(&mut self) -> Result<bool, String> {
1540 if self.is_maximized() {
1541 self.unmaximize_split()?;
1542 Ok(false)
1543 } else {
1544 self.maximize_split()?;
1545 Ok(true)
1546 }
1547 }
1548
1549 pub fn get_splits_in_group(
1551 &self,
1552 group_id: u32,
1553 view_states: &std::collections::HashMap<LeafId, SplitViewState>,
1554 ) -> Vec<LeafId> {
1555 self.root
1556 .leaf_split_ids()
1557 .into_iter()
1558 .filter(|id| {
1559 view_states
1560 .get(id)
1561 .and_then(|vs| vs.sync_group)
1562 .is_some_and(|g| g == group_id)
1563 })
1564 .collect()
1565 }
1566
1567 pub fn set_label(&mut self, split_id: LeafId, label: String) {
1571 self.labels.insert(split_id.into(), label);
1572 }
1573
1574 pub fn clear_label(&mut self, split_id: SplitId) {
1576 self.labels.remove(&split_id);
1577 }
1578
1579 pub fn get_label(&self, split_id: SplitId) -> Option<&str> {
1581 self.labels.get(&split_id).map(|s| s.as_str())
1582 }
1583
1584 pub fn labels(&self) -> &HashMap<SplitId, String> {
1586 &self.labels
1587 }
1588
1589 pub fn find_split_by_label(&self, label: &str) -> Option<LeafId> {
1591 self.root
1592 .leaf_split_ids()
1593 .into_iter()
1594 .find(|id| self.labels.get(&(*id).into()).is_some_and(|l| l == label))
1595 }
1596
1597 pub fn find_unlabeled_leaf(&self) -> Option<LeafId> {
1599 self.root
1600 .leaf_split_ids()
1601 .into_iter()
1602 .find(|id| !self.labels.contains_key(&(*id).into()))
1603 }
1604}
1605
1606#[cfg(test)]
1607mod tests {
1608 use super::*;
1609
1610 #[test]
1611 fn test_create_split_manager() {
1612 let buffer_id = BufferId(0);
1613 let manager = SplitManager::new(buffer_id);
1614
1615 assert_eq!(manager.active_buffer_id(), Some(buffer_id));
1616 assert_eq!(manager.root().count_leaves(), 1);
1617 }
1618
1619 #[test]
1620 fn test_horizontal_split() {
1621 let buffer_a = BufferId(0);
1622 let buffer_b = BufferId(1);
1623
1624 let mut manager = SplitManager::new(buffer_a);
1625 let result = manager.split_active(SplitDirection::Horizontal, buffer_b, 0.5);
1626
1627 assert!(result.is_ok());
1628 assert_eq!(manager.root().count_leaves(), 2);
1629 }
1630
1631 #[test]
1632 fn test_vertical_split() {
1633 let buffer_a = BufferId(0);
1634 let buffer_b = BufferId(1);
1635
1636 let mut manager = SplitManager::new(buffer_a);
1637 let result = manager.split_active(SplitDirection::Vertical, buffer_b, 0.5);
1638
1639 assert!(result.is_ok());
1640 assert_eq!(manager.root().count_leaves(), 2);
1641 }
1642
1643 #[test]
1644 fn test_nested_splits() {
1645 let buffer_a = BufferId(0);
1646 let buffer_b = BufferId(1);
1647 let buffer_c = BufferId(2);
1648
1649 let mut manager = SplitManager::new(buffer_a);
1650
1651 manager
1653 .split_active(SplitDirection::Horizontal, buffer_b, 0.5)
1654 .unwrap();
1655
1656 manager
1658 .split_active(SplitDirection::Vertical, buffer_c, 0.5)
1659 .unwrap();
1660
1661 assert_eq!(manager.root().count_leaves(), 3);
1662 }
1663
1664 #[test]
1665 fn test_close_split() {
1666 let buffer_a = BufferId(0);
1667 let buffer_b = BufferId(1);
1668
1669 let mut manager = SplitManager::new(buffer_a);
1670 let new_split = manager
1671 .split_active(SplitDirection::Horizontal, buffer_b, 0.5)
1672 .unwrap();
1673
1674 assert_eq!(manager.root().count_leaves(), 2);
1675
1676 let result = manager.close_split(new_split);
1678 assert!(result.is_ok());
1679 assert_eq!(manager.root().count_leaves(), 1);
1680 }
1681
1682 #[test]
1683 fn test_cannot_close_last_split() {
1684 let buffer_a = BufferId(0);
1685 let mut manager = SplitManager::new(buffer_a);
1686
1687 let result = manager.close_split(manager.active_split());
1688 assert!(result.is_err());
1689 }
1690
1691 #[test]
1692 fn test_split_rect_horizontal() {
1693 let rect = Rect {
1694 x: 0,
1695 y: 0,
1696 width: 100,
1697 height: 100,
1698 };
1699
1700 let (first, second) = split_rect(rect, SplitDirection::Horizontal, 0.5);
1701
1702 assert_eq!(first.height, 50);
1704 assert_eq!(second.height, 49);
1705 assert_eq!(first.width, 100);
1706 assert_eq!(second.width, 100);
1707 assert_eq!(first.y, 0);
1708 assert_eq!(second.y, 51); }
1710
1711 #[test]
1712 fn test_split_rect_vertical() {
1713 let rect = Rect {
1714 x: 0,
1715 y: 0,
1716 width: 100,
1717 height: 100,
1718 };
1719
1720 let (first, second) = split_rect(rect, SplitDirection::Vertical, 0.5);
1721
1722 assert_eq!(first.width, 50);
1724 assert_eq!(second.width, 49);
1725 assert_eq!(first.height, 100);
1726 assert_eq!(second.height, 100);
1727 assert_eq!(first.x, 0);
1728 assert_eq!(second.x, 51); }
1730
1731 #[test]
1734 fn test_set_and_get_label() {
1735 let mut manager = SplitManager::new(BufferId(0));
1736 let split = manager.active_split();
1737
1738 assert_eq!(manager.get_label(split.into()), None);
1739
1740 manager.set_label(split, "sidebar".to_string());
1741 assert_eq!(manager.get_label(split.into()), Some("sidebar"));
1742 }
1743
1744 #[test]
1745 fn test_clear_label() {
1746 let mut manager = SplitManager::new(BufferId(0));
1747 let split = manager.active_split();
1748
1749 manager.set_label(split, "sidebar".to_string());
1750 assert!(manager.get_label(split.into()).is_some());
1751
1752 manager.clear_label(split.into());
1753 assert_eq!(manager.get_label(split.into()), None);
1754 }
1755
1756 #[test]
1757 fn test_find_split_by_label() {
1758 let mut manager = SplitManager::new(BufferId(0));
1759 let first_split = manager.active_split();
1760
1761 let second_split = manager
1762 .split_active(SplitDirection::Vertical, BufferId(1), 0.5)
1763 .unwrap();
1764
1765 manager.set_label(first_split, "sidebar".to_string());
1766
1767 assert_eq!(manager.find_split_by_label("sidebar"), Some(first_split));
1768 assert_eq!(manager.find_split_by_label("terminal"), None);
1769
1770 assert_ne!(manager.find_split_by_label("sidebar"), Some(second_split));
1772 }
1773
1774 #[test]
1775 fn test_find_unlabeled_leaf() {
1776 let mut manager = SplitManager::new(BufferId(0));
1777 let first_split = manager.active_split();
1778
1779 let second_split = manager
1780 .split_active(SplitDirection::Vertical, BufferId(1), 0.5)
1781 .unwrap();
1782
1783 assert!(manager.find_unlabeled_leaf().is_some());
1785
1786 manager.set_label(first_split, "sidebar".to_string());
1788 assert_eq!(manager.find_unlabeled_leaf(), Some(second_split));
1789
1790 manager.set_label(second_split, "terminal".to_string());
1792 assert_eq!(manager.find_unlabeled_leaf(), None);
1793 }
1794
1795 #[test]
1796 fn test_close_split_cleans_up_label() {
1797 let mut manager = SplitManager::new(BufferId(0));
1798 let _first_split = manager.active_split();
1799
1800 let second_split = manager
1801 .split_active(SplitDirection::Vertical, BufferId(1), 0.5)
1802 .unwrap();
1803
1804 manager.set_label(second_split, "sidebar".to_string());
1805 assert_eq!(manager.find_split_by_label("sidebar"), Some(second_split));
1806
1807 manager.close_split(second_split).unwrap();
1808
1809 assert_eq!(manager.find_split_by_label("sidebar"), None);
1811 assert_eq!(manager.get_label(second_split.into()), None);
1812 }
1813
1814 #[test]
1815 fn test_label_overwrite() {
1816 let mut manager = SplitManager::new(BufferId(0));
1817 let split = manager.active_split();
1818
1819 manager.set_label(split, "sidebar".to_string());
1820 assert_eq!(manager.get_label(split.into()), Some("sidebar"));
1821
1822 manager.set_label(split, "terminal".to_string());
1823 assert_eq!(manager.get_label(split.into()), Some("terminal"));
1824 assert_eq!(manager.find_split_by_label("sidebar"), None);
1825 assert_eq!(manager.find_split_by_label("terminal"), Some(split));
1826 }
1827
1828 #[test]
1829 fn test_find_unlabeled_leaf_single_split_no_label() {
1830 let manager = SplitManager::new(BufferId(0));
1831 assert_eq!(manager.find_unlabeled_leaf(), Some(manager.active_split()));
1833 }
1834
1835 #[test]
1836 fn test_find_unlabeled_leaf_single_split_labeled() {
1837 let mut manager = SplitManager::new(BufferId(0));
1838 let split = manager.active_split();
1839 manager.set_label(split, "only".to_string());
1840 assert_eq!(manager.find_unlabeled_leaf(), None);
1842 }
1843}