1use rust_i18n::t;
11
12use crate::model::event::{BufferId, ContainerId, Event, LeafId, SplitDirection, SplitId};
13use crate::view::folding::CollapsedFoldLineRange;
14use crate::view::split::SplitViewState;
15
16use super::Editor;
17
18impl Editor {
19 pub fn split_pane_horizontal(&mut self) {
21 self.split_pane_impl(crate::model::event::SplitDirection::Horizontal);
22 }
23
24 pub fn split_pane_vertical(&mut self) {
26 self.split_pane_impl(crate::model::event::SplitDirection::Vertical);
27 }
28
29 fn split_pane_impl(&mut self, direction: crate::model::event::SplitDirection) {
31 let current_buffer_id = self.active_buffer();
32 let active_split = self.split_manager.active_split();
33
34 let source_keyed_states = self.split_view_states.get(&active_split).map(|vs| {
36 vs.keyed_states
37 .iter()
38 .filter(|(&buf_id, _)| buf_id != current_buffer_id)
39 .map(|(&buf_id, buf_state)| {
40 let folds = self
41 .buffers
42 .get(&buf_id)
43 .map(|state| {
44 buf_state
45 .folds
46 .collapsed_line_ranges(&state.buffer, &state.marker_list)
47 })
48 .unwrap_or_default();
49 (buf_id, buf_state.clone(), folds)
50 })
51 .collect::<Vec<(
52 BufferId,
53 crate::view::split::BufferViewState,
54 Vec<CollapsedFoldLineRange>,
55 )>>()
56 });
57
58 match self
59 .split_manager
60 .split_active(direction, current_buffer_id, 0.5)
61 {
62 Ok(new_split_id) => {
63 let mut view_state = SplitViewState::with_buffer(
64 self.terminal_width,
65 self.terminal_height,
66 current_buffer_id,
67 );
68 view_state.apply_config_defaults(
69 self.config.editor.line_numbers,
70 self.config.editor.line_wrap,
71 self.config.editor.wrap_indent,
72 self.config.editor.rulers.clone(),
73 );
74
75 if let Some(source) = source_keyed_states {
78 for (buf_id, mut buf_state, folds) in source {
79 if let Some(state) = self.buffers.get_mut(&buf_id) {
80 buf_state.folds.clear(&mut state.marker_list);
81 for fold in folds {
82 let start_line = fold.header_line.saturating_add(1);
83 let end_line = fold.end_line;
84 if start_line > end_line {
85 continue;
86 }
87 let Some(start_byte) = state.buffer.line_start_offset(start_line)
88 else {
89 continue;
90 };
91 let end_byte = state
92 .buffer
93 .line_start_offset(end_line.saturating_add(1))
94 .unwrap_or_else(|| state.buffer.len());
95 buf_state.folds.add(
96 &mut state.marker_list,
97 start_byte,
98 end_byte,
99 fold.placeholder.clone(),
100 );
101 }
102 }
103 view_state.keyed_states.insert(buf_id, buf_state);
104 }
105 }
106
107 self.split_view_states.insert(new_split_id, view_state);
108 let msg = match direction {
109 crate::model::event::SplitDirection::Horizontal => t!("split.horizontal"),
110 crate::model::event::SplitDirection::Vertical => t!("split.vertical"),
111 };
112 self.set_status_message(msg.to_string());
113 }
114 Err(e) => {
115 self.set_status_message(t!("split.error", error = e.to_string()).to_string());
116 }
117 }
118 }
119
120 pub fn close_active_split(&mut self) {
122 let closing_split = self.split_manager.active_split();
123
124 let closing_split_tabs = self
126 .split_view_states
127 .get(&closing_split)
128 .map(|vs| vs.open_buffers.clone())
129 .unwrap_or_default();
130
131 match self.split_manager.close_split(closing_split) {
132 Ok(_) => {
133 self.split_view_states.remove(&closing_split);
135
136 let new_active_split = self.split_manager.active_split();
138
139 if let Some(view_state) = self.split_view_states.get_mut(&new_active_split) {
141 for buffer_id in closing_split_tabs {
142 if !view_state.open_buffers.contains(&buffer_id) {
144 view_state.open_buffers.push(buffer_id);
145 }
146 }
147 }
148
149 self.set_status_message(t!("split.closed").to_string());
152 }
153 Err(e) => {
154 self.set_status_message(
155 t!("split.cannot_close", error = e.to_string()).to_string(),
156 );
157 }
158 }
159 }
160
161 pub fn next_split(&mut self) {
163 self.switch_split(true);
164 self.set_status_message(t!("split.next").to_string());
165 }
166
167 pub fn prev_split(&mut self) {
169 self.switch_split(false);
170 self.set_status_message(t!("split.prev").to_string());
171 }
172
173 fn switch_split(&mut self, next: bool) {
175 if next {
176 self.split_manager.next_split();
177 } else {
178 self.split_manager.prev_split();
179 }
180
181 let split_id = self.split_manager.active_split();
183 self.ensure_active_tab_visible(split_id, self.active_buffer(), self.effective_tabs_width());
184
185 let buffer_id = self.active_buffer();
186
187 self.plugin_manager.run_hook(
189 "buffer_activated",
190 crate::services::plugins::hooks::HookArgs::BufferActivated { buffer_id },
191 );
192
193 if self.is_terminal_buffer(buffer_id) {
195 self.terminal_mode = true;
196 self.key_context = crate::input::keybindings::KeyContext::Terminal;
197 }
198 }
199
200 pub(crate) fn adjust_other_split_cursors_for_event(&mut self, event: &Event) {
202 if let Event::BulkEdit { new_cursors, .. } = event {
204 let current_buffer_id = self.active_buffer();
206 let current_split_id = self.split_manager.active_split();
207
208 let splits_for_buffer = self.split_manager.splits_for_buffer(current_buffer_id);
210
211 let buffer_len = self
213 .buffers
214 .get(¤t_buffer_id)
215 .map(|s| s.buffer.len())
216 .unwrap_or(0);
217
218 for split_id in splits_for_buffer {
220 if split_id == current_split_id {
221 continue;
222 }
223
224 if let Some(view_state) = self.split_view_states.get_mut(&split_id) {
225 if let Some((_, pos, _)) = new_cursors.first() {
227 let new_pos = (*pos).min(buffer_len);
228 view_state.cursors.primary_mut().position = new_pos;
229 view_state.cursors.primary_mut().anchor = None;
230 }
231 }
232 }
233 return;
234 }
235
236 let adjustments = match event {
238 Event::Insert { position, text, .. } => {
239 vec![(*position, 0, text.len())]
240 }
241 Event::Delete { range, .. } => {
242 vec![(range.start, range.len(), 0)]
243 }
244 Event::Batch { events, .. } => {
245 events
247 .iter()
248 .filter_map(|e| match e {
249 Event::Insert { position, text, .. } => Some((*position, 0, text.len())),
250 Event::Delete { range, .. } => Some((range.start, range.len(), 0)),
251 _ => None,
252 })
253 .collect()
254 }
255 _ => vec![],
256 };
257
258 if adjustments.is_empty() {
259 return;
260 }
261
262 let current_buffer_id = self.active_buffer();
264 let current_split_id = self.split_manager.active_split();
265
266 let splits_for_buffer = self.split_manager.splits_for_buffer(current_buffer_id);
268
269 for split_id in splits_for_buffer {
271 if split_id == current_split_id {
272 continue; }
274
275 if let Some(view_state) = self.split_view_states.get_mut(&split_id) {
276 for (edit_pos, old_len, new_len) in &adjustments {
277 view_state
278 .cursors
279 .adjust_for_edit(*edit_pos, *old_len, *new_len);
280 }
281 }
282 }
283 }
284
285 pub fn adjust_split_size(&mut self, delta: f32) {
287 let active_split = self.split_manager.active_split();
288 if let Some(container) = self.split_manager.parent_container_of(active_split) {
289 self.split_manager.adjust_ratio(container, delta);
290
291 let percent = (delta * 100.0) as i32;
292 self.set_status_message(t!("split.size_adjusted", percent = percent).to_string());
293 self.resize_visible_terminals();
295 }
296 }
297
298 pub fn toggle_maximize_split(&mut self) {
300 match self.split_manager.toggle_maximize() {
301 Ok(maximized) => {
302 if maximized {
303 self.set_status_message(t!("split.maximized").to_string());
304 } else {
305 self.set_status_message(t!("split.restored").to_string());
306 }
307 self.resize_visible_terminals();
309 }
310 Err(e) => self.set_status_message(e),
311 }
312 }
313
314 pub fn get_separator_areas(&self) -> &[(ContainerId, SplitDirection, u16, u16, u16)] {
317 &self.cached_layout.separator_areas
318 }
319
320 pub fn get_tab_layouts(
322 &self,
323 ) -> &std::collections::HashMap<LeafId, crate::view::ui::tabs::TabLayout> {
324 &self.cached_layout.tab_layouts
325 }
326
327 pub fn get_split_areas(
330 &self,
331 ) -> &[(
332 LeafId,
333 BufferId,
334 ratatui::layout::Rect,
335 ratatui::layout::Rect,
336 usize,
337 usize,
338 )] {
339 &self.cached_layout.split_areas
340 }
341
342 pub fn get_split_ratio(&self, split_id: SplitId) -> Option<f32> {
344 self.split_manager.get_ratio(split_id)
345 }
346
347 pub fn get_active_split(&self) -> LeafId {
349 self.split_manager.active_split()
350 }
351
352 pub fn get_split_buffer(&self, split_id: SplitId) -> Option<BufferId> {
354 self.split_manager.get_buffer_id(split_id)
355 }
356
357 pub fn get_split_tabs(&self, split_id: LeafId) -> Vec<BufferId> {
359 self.split_view_states
360 .get(&split_id)
361 .map(|vs| vs.open_buffers.clone())
362 .unwrap_or_default()
363 }
364
365 pub fn get_split_count(&self) -> usize {
367 self.split_manager.root().count_leaves()
368 }
369
370 pub fn compute_drop_zone(
372 &self,
373 col: u16,
374 row: u16,
375 source_split_id: LeafId,
376 ) -> Option<super::types::TabDropZone> {
377 self.compute_tab_drop_zone(col, row, source_split_id)
378 }
379}