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 if next {
190 self.split_manager.next_split();
191 } else {
192 self.split_manager.prev_split();
193 }
194
195 let split_id = self.split_manager.active_split();
197 self.promote_preview_if_not_in_split(split_id);
200 self.ensure_active_tab_visible(split_id, self.active_buffer(), self.effective_tabs_width());
201
202 let buffer_id = self.active_buffer();
203
204 self.plugin_manager.run_hook(
206 "buffer_activated",
207 crate::services::plugins::hooks::HookArgs::BufferActivated { buffer_id },
208 );
209
210 if self.is_terminal_buffer(buffer_id) {
212 self.terminal_mode = true;
213 self.key_context = crate::input::keybindings::KeyContext::Terminal;
214 }
215 }
216
217 pub(crate) fn adjust_other_split_cursors_for_event(&mut self, event: &Event) {
219 if let Event::BulkEdit { new_cursors, .. } = event {
221 let current_buffer_id = self.active_buffer();
223 let current_split_id = self.split_manager.active_split();
224
225 let splits_for_buffer = self.split_manager.splits_for_buffer(current_buffer_id);
227
228 let buffer_len = self
230 .buffers
231 .get(¤t_buffer_id)
232 .map(|s| s.buffer.len())
233 .unwrap_or(0);
234
235 for split_id in splits_for_buffer {
237 if split_id == current_split_id {
238 continue;
239 }
240
241 if let Some(view_state) = self.split_view_states.get_mut(&split_id) {
242 if let Some((_, pos, _)) = new_cursors.first() {
244 let new_pos = (*pos).min(buffer_len);
245 view_state.cursors.primary_mut().position = new_pos;
246 view_state.cursors.primary_mut().anchor = None;
247 }
248 }
249 }
250 return;
251 }
252
253 let adjustments = match event {
255 Event::Insert { position, text, .. } => {
256 vec![(*position, 0, text.len())]
257 }
258 Event::Delete { range, .. } => {
259 vec![(range.start, range.len(), 0)]
260 }
261 Event::Batch { events, .. } => {
262 events
264 .iter()
265 .filter_map(|e| match e {
266 Event::Insert { position, text, .. } => Some((*position, 0, text.len())),
267 Event::Delete { range, .. } => Some((range.start, range.len(), 0)),
268 _ => None,
269 })
270 .collect()
271 }
272 _ => vec![],
273 };
274
275 if adjustments.is_empty() {
276 return;
277 }
278
279 let current_buffer_id = self.active_buffer();
281 let current_split_id = self.split_manager.active_split();
282
283 let splits_for_buffer = self.split_manager.splits_for_buffer(current_buffer_id);
285
286 for split_id in splits_for_buffer {
288 if split_id == current_split_id {
289 continue; }
291
292 if let Some(view_state) = self.split_view_states.get_mut(&split_id) {
293 for (edit_pos, old_len, new_len) in &adjustments {
294 view_state
295 .cursors
296 .adjust_for_edit(*edit_pos, *old_len, *new_len);
297 }
298 }
299 }
300 }
301
302 pub fn adjust_split_size(&mut self, delta: f32) {
304 let active_split = self.split_manager.active_split();
305 if let Some(container) = self.split_manager.parent_container_of(active_split) {
306 self.split_manager.adjust_ratio(container, delta);
307
308 let percent = (delta * 100.0) as i32;
309 self.set_status_message(t!("split.size_adjusted", percent = percent).to_string());
310 self.resize_visible_terminals();
312 }
313 }
314
315 pub fn toggle_maximize_split(&mut self) {
317 match self.split_manager.toggle_maximize() {
318 Ok(maximized) => {
319 if maximized {
320 self.set_status_message(t!("split.maximized").to_string());
321 } else {
322 self.set_status_message(t!("split.restored").to_string());
323 }
324 self.resize_visible_terminals();
326 }
327 Err(e) => self.set_status_message(e),
328 }
329 }
330
331 pub fn get_separator_areas(&self) -> &[(ContainerId, SplitDirection, u16, u16, u16)] {
334 &self.cached_layout.separator_areas
335 }
336
337 pub fn get_tab_layouts(
339 &self,
340 ) -> &std::collections::HashMap<LeafId, crate::view::ui::tabs::TabLayout> {
341 &self.cached_layout.tab_layouts
342 }
343
344 pub fn get_split_areas(
347 &self,
348 ) -> &[(
349 LeafId,
350 BufferId,
351 ratatui::layout::Rect,
352 ratatui::layout::Rect,
353 usize,
354 usize,
355 )] {
356 &self.cached_layout.split_areas
357 }
358
359 pub fn get_split_ratio(&self, split_id: SplitId) -> Option<f32> {
364 self.split_manager
365 .get_ratio(split_id)
366 .or_else(|| self.grouped_split_ratio(crate::model::event::ContainerId(split_id)))
367 }
368
369 pub fn get_active_split(&self) -> LeafId {
371 self.split_manager.active_split()
372 }
373
374 pub fn get_split_buffer(&self, split_id: SplitId) -> Option<BufferId> {
376 self.split_manager.get_buffer_id(split_id)
377 }
378
379 pub fn get_split_tabs(&self, split_id: LeafId) -> Vec<BufferId> {
381 self.split_view_states
382 .get(&split_id)
383 .map(|vs| vs.buffer_tab_ids_vec())
384 .unwrap_or_default()
385 }
386
387 pub fn get_split_count(&self) -> usize {
389 self.split_manager.root().count_leaves()
390 }
391
392 pub fn compute_drop_zone(
394 &self,
395 col: u16,
396 row: u16,
397 source_split_id: LeafId,
398 ) -> Option<super::types::TabDropZone> {
399 self.compute_tab_drop_zone(col, row, source_split_id)
400 }
401}