rmux_core/session/pane_transfer/
break_window.rs1use super::super::pane_transfer_shared::resolve_break_destination_index;
2use super::super::target_error::{invalid_pane_target, invalid_window_target};
3use super::super::{BreakPaneOptions, Session, SessionPaneTarget};
4use crate::Window;
5use rmux_proto::RmuxError;
6
7impl Session {
8 pub fn break_pane(
10 &mut self,
11 source: SessionPaneTarget,
12 options: BreakPaneOptions,
13 ) -> Result<u32, RmuxError> {
14 let original_source_window = self
15 .window_at(source.window_index)
16 .ok_or_else(|| invalid_window_target(&self.name, source.window_index))?;
17 if original_source_window.pane(source.pane_index).is_none() {
18 return Err(invalid_pane_target(
19 &self.name,
20 source.window_index,
21 source.pane_index,
22 "pane index does not exist in session",
23 ));
24 }
25 let source_is_single_pane = original_source_window.pane_count() == 1;
26 let (destination_index, source_window_index) = prepare_break_destination(
27 self,
28 source.window_index,
29 options.target_window_index,
30 options.after,
31 options.before,
32 )?;
33
34 if source_is_single_pane {
35 self.window_at_mut(source_window_index)
36 .expect("prepared source window must exist")
37 .renumber_single_pane_to_zero();
38 self.move_window(
39 source_window_index,
40 destination_index,
41 false,
42 !options.detached,
43 )?;
44 if let Some(name) = options.name {
45 self.rename_window(destination_index, name)?;
46 }
47 return Ok(destination_index);
48 }
49
50 let source_size = self
51 .window_at(source_window_index)
52 .expect("prepared source window must exist")
53 .size();
54 let source_window = self
55 .window_at_mut(source_window_index)
56 .expect("source window must exist");
57 source_window.auto_unzoom();
58 let moved_pane = source_window
59 .extract_pane(source.pane_index)
60 .expect("validated source pane must extract");
61 let mut new_window =
62 Window::new_with_initial_pane(source_size, moved_pane.id(), self.allocate_window_id());
63 if let Some(name) = options.name {
64 new_window.set_name(name);
65 }
66 self.insert_existing_window(destination_index, new_window)?;
67 if !options.detached {
68 self.select_window(destination_index)?;
69 }
70
71 Ok(destination_index)
72 }
73}
74
75fn prepare_break_destination(
76 session: &mut Session,
77 source_window_index: u32,
78 target_window_index: Option<u32>,
79 after: bool,
80 before: bool,
81) -> Result<(u32, u32), RmuxError> {
82 if !(after || before) {
83 let destination_index = resolve_break_destination_index(
84 session,
85 target_window_index,
86 Some(source_window_index),
87 )?;
88 return Ok((destination_index, source_window_index));
89 }
90
91 let anchor_index = target_window_index.unwrap_or(session.active_window_index());
92 if target_window_index.is_some() && session.window_at(anchor_index).is_none() {
93 return Err(invalid_window_target(&session.name, anchor_index));
94 }
95 let destination_index = if before {
96 anchor_index
97 } else {
98 anchor_index
99 .checked_add(1)
100 .ok_or_else(|| RmuxError::Server("window index space exhausted".to_owned()))?
101 };
102 shift_windows_up_from(session, destination_index)?;
103 let shifted_source = if source_window_index >= destination_index {
104 source_window_index
105 .checked_add(1)
106 .ok_or_else(|| RmuxError::Server("window index space exhausted".to_owned()))?
107 } else {
108 source_window_index
109 };
110 Ok((destination_index, shifted_source))
111}
112
113fn shift_windows_up_from(session: &mut Session, start_index: u32) -> Result<(), RmuxError> {
114 if session
115 .windows
116 .keys()
117 .next_back()
118 .is_some_and(|window_index| *window_index == u32::MAX)
119 {
120 return Err(RmuxError::Server("window index space exhausted".to_owned()));
121 }
122
123 let shifted_windows = session
124 .windows
125 .range(start_index..)
126 .map(|(window_index, _)| *window_index)
127 .collect::<Vec<_>>();
128 for window_index in shifted_windows.into_iter().rev() {
129 let new_index = window_index
130 .checked_add(1)
131 .ok_or_else(|| RmuxError::Server("window index space exhausted".to_owned()))?;
132 let window = session
133 .windows
134 .remove(&window_index)
135 .expect("shifted window must exist");
136 let flags = session
137 .winlink_alert_flags
138 .remove(&window_index)
139 .unwrap_or_else(crate::AlertFlags::empty);
140 let replaced_window = session.windows.insert(new_index, window);
141 debug_assert!(replaced_window.is_none());
142 let replaced_flags = session.winlink_alert_flags.insert(new_index, flags);
143 debug_assert!(replaced_flags.is_none());
144 }
145 if session.active_window >= start_index {
146 session.active_window = session.active_window.saturating_add(1);
147 }
148 if let Some(last_window) = session.last_window.filter(|index| *index >= start_index) {
149 session.last_window = Some(last_window.saturating_add(1));
150 }
151 Ok(())
152}