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 self.promote_current_preview();
36
37 let current_buffer_id = self.active_buffer();
38 let active_split = self.split_manager.active_split();
39
40 let source_keyed_states = self.split_view_states.get(&active_split).map(|vs| {
42 vs.keyed_states
43 .iter()
44 .filter(|(&buf_id, _)| buf_id != current_buffer_id)
45 .map(|(&buf_id, buf_state)| {
46 let folds = self
47 .buffers
48 .get(&buf_id)
49 .map(|state| {
50 buf_state
51 .folds
52 .collapsed_line_ranges(&state.buffer, &state.marker_list)
53 })
54 .unwrap_or_default();
55 (buf_id, buf_state.clone(), folds)
56 })
57 .collect::<Vec<(
58 BufferId,
59 crate::view::split::BufferViewState,
60 Vec<CollapsedFoldLineRange>,
61 )>>()
62 });
63
64 match self
65 .split_manager
66 .split_active(direction, current_buffer_id, 0.5)
67 {
68 Ok(new_split_id) => {
69 let mut view_state = SplitViewState::with_buffer(
70 self.terminal_width,
71 self.terminal_height,
72 current_buffer_id,
73 );
74 view_state.apply_config_defaults(
75 self.config.editor.line_numbers,
76 self.config.editor.highlight_current_line,
77 self.resolve_line_wrap_for_buffer(current_buffer_id),
78 self.config.editor.wrap_indent,
79 self.resolve_wrap_column_for_buffer(current_buffer_id),
80 self.config.editor.rulers.clone(),
81 );
82
83 if let Some(source) = source_keyed_states {
86 for (buf_id, mut buf_state, folds) in source {
87 if let Some(state) = self.buffers.get_mut(&buf_id) {
88 buf_state.folds.clear(&mut state.marker_list);
89 for fold in folds {
90 let start_line = fold.header_line.saturating_add(1);
91 let end_line = fold.end_line;
92 if start_line > end_line {
93 continue;
94 }
95 let Some(start_byte) = state.buffer.line_start_offset(start_line)
96 else {
97 continue;
98 };
99 let end_byte = state
100 .buffer
101 .line_start_offset(end_line.saturating_add(1))
102 .unwrap_or_else(|| state.buffer.len());
103 buf_state.folds.add(
104 &mut state.marker_list,
105 start_byte,
106 end_byte,
107 fold.placeholder.clone(),
108 );
109 }
110 }
111 view_state.keyed_states.insert(buf_id, buf_state);
112 }
113 }
114
115 self.split_view_states.insert(new_split_id, view_state);
116 let msg = match direction {
117 crate::model::event::SplitDirection::Horizontal => t!("split.horizontal"),
118 crate::model::event::SplitDirection::Vertical => t!("split.vertical"),
119 };
120 self.set_status_message(msg.to_string());
121 }
122 Err(e) => {
123 self.set_status_message(t!("split.error", error = e.to_string()).to_string());
124 }
125 }
126 }
127
128 pub fn close_active_split(&mut self) {
130 self.promote_current_preview();
135
136 let closing_split = self.split_manager.active_split();
137
138 let closing_split_tabs = self
140 .split_view_states
141 .get(&closing_split)
142 .map(|vs| vs.open_buffers.clone())
143 .unwrap_or_default();
144
145 match self.split_manager.close_split(closing_split) {
146 Ok(_) => {
147 self.split_view_states.remove(&closing_split);
149
150 let new_active_split = self.split_manager.active_split();
152
153 if let Some(view_state) = self.split_view_states.get_mut(&new_active_split) {
155 for target in closing_split_tabs {
156 if !view_state.open_buffers.contains(&target) {
158 view_state.open_buffers.push(target);
159 }
160 }
161 }
162
163 self.set_status_message(t!("split.closed").to_string());
166 }
167 Err(e) => {
168 self.set_status_message(
169 t!("split.cannot_close", error = e.to_string()).to_string(),
170 );
171 }
172 }
173 }
174
175 pub fn next_split(&mut self) {
177 self.switch_split(true);
178 self.set_status_message(t!("split.next").to_string());
179 }
180
181 pub fn prev_split(&mut self) {
183 self.switch_split(false);
184 self.set_status_message(t!("split.prev").to_string());
185 }
186
187 fn switch_split(&mut self, next: bool) {
189 let previous_buffer = self.active_buffer();
195
196 if next {
197 self.split_manager.next_split();
198 } else {
199 self.split_manager.prev_split();
200 }
201
202 let split_id = self.split_manager.active_split();
204 self.promote_preview_if_not_in_split(split_id);
207 self.ensure_active_tab_visible(split_id, self.active_buffer(), self.effective_tabs_width());
208
209 let buffer_id = self.active_buffer();
210
211 if self.terminal_mode
214 && self.is_terminal_buffer(previous_buffer)
215 && !self.is_terminal_buffer(buffer_id)
216 {
217 self.terminal_mode = false;
218 self.key_context = crate::input::keybindings::KeyContext::Normal;
219 }
220
221 self.plugin_manager.run_hook(
223 "buffer_activated",
224 crate::services::plugins::hooks::HookArgs::BufferActivated { buffer_id },
225 );
226
227 if self.is_terminal_buffer(buffer_id) {
229 self.terminal_mode = true;
230 self.key_context = crate::input::keybindings::KeyContext::Terminal;
231 }
232 }
233
234 pub(crate) fn adjust_other_split_cursors_for_event(&mut self, event: &Event) {
236 if let Event::BulkEdit { new_cursors, .. } = event {
238 let current_buffer_id = self.active_buffer();
240 let current_split_id = self.split_manager.active_split();
241
242 let splits_for_buffer = self.split_manager.splits_for_buffer(current_buffer_id);
244
245 let buffer_len = self
247 .buffers
248 .get(¤t_buffer_id)
249 .map(|s| s.buffer.len())
250 .unwrap_or(0);
251
252 for split_id in splits_for_buffer {
254 if split_id == current_split_id {
255 continue;
256 }
257
258 if let Some(view_state) = self.split_view_states.get_mut(&split_id) {
259 if let Some((_, pos, _)) = new_cursors.first() {
261 let new_pos = (*pos).min(buffer_len);
262 view_state.cursors.primary_mut().position = new_pos;
263 view_state.cursors.primary_mut().anchor = None;
264 }
265 }
266 }
267 return;
268 }
269
270 let adjustments = match event {
272 Event::Insert { position, text, .. } => {
273 vec![(*position, 0, text.len())]
274 }
275 Event::Delete { range, .. } => {
276 vec![(range.start, range.len(), 0)]
277 }
278 Event::Batch { events, .. } => {
279 events
281 .iter()
282 .filter_map(|e| match e {
283 Event::Insert { position, text, .. } => Some((*position, 0, text.len())),
284 Event::Delete { range, .. } => Some((range.start, range.len(), 0)),
285 _ => None,
286 })
287 .collect()
288 }
289 _ => vec![],
290 };
291
292 if adjustments.is_empty() {
293 return;
294 }
295
296 let current_buffer_id = self.active_buffer();
298 let current_split_id = self.split_manager.active_split();
299
300 let splits_for_buffer = self.split_manager.splits_for_buffer(current_buffer_id);
302
303 for split_id in splits_for_buffer {
305 if split_id == current_split_id {
306 continue; }
308
309 if let Some(view_state) = self.split_view_states.get_mut(&split_id) {
310 for (edit_pos, old_len, new_len) in &adjustments {
311 view_state
312 .cursors
313 .adjust_for_edit(*edit_pos, *old_len, *new_len);
314 }
315 }
316 }
317 }
318
319 pub fn adjust_split_size(&mut self, delta: f32) {
321 let active_split = self.split_manager.active_split();
322 if let Some(container) = self.split_manager.parent_container_of(active_split) {
323 self.split_manager.adjust_ratio(container, delta);
324
325 let percent = (delta * 100.0) as i32;
326 self.set_status_message(t!("split.size_adjusted", percent = percent).to_string());
327 self.resize_visible_terminals();
329 }
330 }
331
332 pub fn toggle_maximize_split(&mut self) {
334 match self.split_manager.toggle_maximize() {
335 Ok(maximized) => {
336 if maximized {
337 self.set_status_message(t!("split.maximized").to_string());
338 } else {
339 self.set_status_message(t!("split.restored").to_string());
340 }
341 self.resize_visible_terminals();
343 }
344 Err(e) => self.set_status_message(e),
345 }
346 }
347
348 pub fn get_separator_areas(&self) -> &[(ContainerId, SplitDirection, u16, u16, u16)] {
351 &self.cached_layout.separator_areas
352 }
353
354 pub fn get_tab_layouts(
356 &self,
357 ) -> &std::collections::HashMap<LeafId, crate::view::ui::tabs::TabLayout> {
358 &self.cached_layout.tab_layouts
359 }
360
361 pub fn get_split_areas(
364 &self,
365 ) -> &[(
366 LeafId,
367 BufferId,
368 ratatui::layout::Rect,
369 ratatui::layout::Rect,
370 usize,
371 usize,
372 )] {
373 &self.cached_layout.split_areas
374 }
375
376 pub fn get_split_ratio(&self, split_id: SplitId) -> Option<f32> {
381 self.split_manager
382 .get_ratio(split_id)
383 .or_else(|| self.grouped_split_ratio(crate::model::event::ContainerId(split_id)))
384 }
385
386 pub fn get_active_split(&self) -> LeafId {
388 self.split_manager.active_split()
389 }
390
391 pub fn get_split_buffer(&self, split_id: SplitId) -> Option<BufferId> {
393 self.split_manager.get_buffer_id(split_id)
394 }
395
396 pub fn get_split_tabs(&self, split_id: LeafId) -> Vec<BufferId> {
398 self.split_view_states
399 .get(&split_id)
400 .map(|vs| vs.buffer_tab_ids_vec())
401 .unwrap_or_default()
402 }
403
404 pub fn get_split_count(&self) -> usize {
406 self.split_manager.root().count_leaves()
407 }
408
409 pub fn compute_drop_zone(
411 &self,
412 col: u16,
413 row: u16,
414 source_split_id: LeafId,
415 ) -> Option<super::types::TabDropZone> {
416 self.compute_tab_drop_zone(col, row, source_split_id)
417 }
418}