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.highlight_current_line,
71 self.resolve_line_wrap_for_buffer(current_buffer_id),
72 self.config.editor.wrap_indent,
73 self.resolve_wrap_column_for_buffer(current_buffer_id),
74 self.config.editor.rulers.clone(),
75 );
76
77 if let Some(source) = source_keyed_states {
80 for (buf_id, mut buf_state, folds) in source {
81 if let Some(state) = self.buffers.get_mut(&buf_id) {
82 buf_state.folds.clear(&mut state.marker_list);
83 for fold in folds {
84 let start_line = fold.header_line.saturating_add(1);
85 let end_line = fold.end_line;
86 if start_line > end_line {
87 continue;
88 }
89 let Some(start_byte) = state.buffer.line_start_offset(start_line)
90 else {
91 continue;
92 };
93 let end_byte = state
94 .buffer
95 .line_start_offset(end_line.saturating_add(1))
96 .unwrap_or_else(|| state.buffer.len());
97 buf_state.folds.add(
98 &mut state.marker_list,
99 start_byte,
100 end_byte,
101 fold.placeholder.clone(),
102 );
103 }
104 }
105 view_state.keyed_states.insert(buf_id, buf_state);
106 }
107 }
108
109 self.split_view_states.insert(new_split_id, view_state);
110 let msg = match direction {
111 crate::model::event::SplitDirection::Horizontal => t!("split.horizontal"),
112 crate::model::event::SplitDirection::Vertical => t!("split.vertical"),
113 };
114 self.set_status_message(msg.to_string());
115 }
116 Err(e) => {
117 self.set_status_message(t!("split.error", error = e.to_string()).to_string());
118 }
119 }
120 }
121
122 pub fn close_active_split(&mut self) {
124 let closing_split = self.split_manager.active_split();
125
126 let closing_split_tabs = self
128 .split_view_states
129 .get(&closing_split)
130 .map(|vs| vs.open_buffers.clone())
131 .unwrap_or_default();
132
133 match self.split_manager.close_split(closing_split) {
134 Ok(_) => {
135 self.split_view_states.remove(&closing_split);
137
138 let new_active_split = self.split_manager.active_split();
140
141 if let Some(view_state) = self.split_view_states.get_mut(&new_active_split) {
143 for buffer_id in closing_split_tabs {
144 if !view_state.open_buffers.contains(&buffer_id) {
146 view_state.open_buffers.push(buffer_id);
147 }
148 }
149 }
150
151 self.set_status_message(t!("split.closed").to_string());
154 }
155 Err(e) => {
156 self.set_status_message(
157 t!("split.cannot_close", error = e.to_string()).to_string(),
158 );
159 }
160 }
161 }
162
163 pub fn next_split(&mut self) {
165 self.switch_split(true);
166 self.set_status_message(t!("split.next").to_string());
167 }
168
169 pub fn prev_split(&mut self) {
171 self.switch_split(false);
172 self.set_status_message(t!("split.prev").to_string());
173 }
174
175 fn switch_split(&mut self, next: bool) {
177 if next {
178 self.split_manager.next_split();
179 } else {
180 self.split_manager.prev_split();
181 }
182
183 let split_id = self.split_manager.active_split();
185 self.ensure_active_tab_visible(split_id, self.active_buffer(), self.effective_tabs_width());
186
187 let buffer_id = self.active_buffer();
188
189 self.plugin_manager.run_hook(
191 "buffer_activated",
192 crate::services::plugins::hooks::HookArgs::BufferActivated { buffer_id },
193 );
194
195 if self.is_terminal_buffer(buffer_id) {
197 self.terminal_mode = true;
198 self.key_context = crate::input::keybindings::KeyContext::Terminal;
199 }
200 }
201
202 pub(crate) fn adjust_other_split_cursors_for_event(&mut self, event: &Event) {
204 if let Event::BulkEdit { new_cursors, .. } = event {
206 let current_buffer_id = self.active_buffer();
208 let current_split_id = self.split_manager.active_split();
209
210 let splits_for_buffer = self.split_manager.splits_for_buffer(current_buffer_id);
212
213 let buffer_len = self
215 .buffers
216 .get(¤t_buffer_id)
217 .map(|s| s.buffer.len())
218 .unwrap_or(0);
219
220 for split_id in splits_for_buffer {
222 if split_id == current_split_id {
223 continue;
224 }
225
226 if let Some(view_state) = self.split_view_states.get_mut(&split_id) {
227 if let Some((_, pos, _)) = new_cursors.first() {
229 let new_pos = (*pos).min(buffer_len);
230 view_state.cursors.primary_mut().position = new_pos;
231 view_state.cursors.primary_mut().anchor = None;
232 }
233 }
234 }
235 return;
236 }
237
238 let adjustments = match event {
240 Event::Insert { position, text, .. } => {
241 vec![(*position, 0, text.len())]
242 }
243 Event::Delete { range, .. } => {
244 vec![(range.start, range.len(), 0)]
245 }
246 Event::Batch { events, .. } => {
247 events
249 .iter()
250 .filter_map(|e| match e {
251 Event::Insert { position, text, .. } => Some((*position, 0, text.len())),
252 Event::Delete { range, .. } => Some((range.start, range.len(), 0)),
253 _ => None,
254 })
255 .collect()
256 }
257 _ => vec![],
258 };
259
260 if adjustments.is_empty() {
261 return;
262 }
263
264 let current_buffer_id = self.active_buffer();
266 let current_split_id = self.split_manager.active_split();
267
268 let splits_for_buffer = self.split_manager.splits_for_buffer(current_buffer_id);
270
271 for split_id in splits_for_buffer {
273 if split_id == current_split_id {
274 continue; }
276
277 if let Some(view_state) = self.split_view_states.get_mut(&split_id) {
278 for (edit_pos, old_len, new_len) in &adjustments {
279 view_state
280 .cursors
281 .adjust_for_edit(*edit_pos, *old_len, *new_len);
282 }
283 }
284 }
285 }
286
287 pub fn adjust_split_size(&mut self, delta: f32) {
289 let active_split = self.split_manager.active_split();
290 if let Some(container) = self.split_manager.parent_container_of(active_split) {
291 self.split_manager.adjust_ratio(container, delta);
292
293 let percent = (delta * 100.0) as i32;
294 self.set_status_message(t!("split.size_adjusted", percent = percent).to_string());
295 self.resize_visible_terminals();
297 }
298 }
299
300 pub fn toggle_maximize_split(&mut self) {
302 match self.split_manager.toggle_maximize() {
303 Ok(maximized) => {
304 if maximized {
305 self.set_status_message(t!("split.maximized").to_string());
306 } else {
307 self.set_status_message(t!("split.restored").to_string());
308 }
309 self.resize_visible_terminals();
311 }
312 Err(e) => self.set_status_message(e),
313 }
314 }
315
316 pub fn get_separator_areas(&self) -> &[(ContainerId, SplitDirection, u16, u16, u16)] {
319 &self.cached_layout.separator_areas
320 }
321
322 pub fn get_tab_layouts(
324 &self,
325 ) -> &std::collections::HashMap<LeafId, crate::view::ui::tabs::TabLayout> {
326 &self.cached_layout.tab_layouts
327 }
328
329 pub fn get_split_areas(
332 &self,
333 ) -> &[(
334 LeafId,
335 BufferId,
336 ratatui::layout::Rect,
337 ratatui::layout::Rect,
338 usize,
339 usize,
340 )] {
341 &self.cached_layout.split_areas
342 }
343
344 pub fn get_split_ratio(&self, split_id: SplitId) -> Option<f32> {
346 self.split_manager.get_ratio(split_id)
347 }
348
349 pub fn get_active_split(&self) -> LeafId {
351 self.split_manager.active_split()
352 }
353
354 pub fn get_split_buffer(&self, split_id: SplitId) -> Option<BufferId> {
356 self.split_manager.get_buffer_id(split_id)
357 }
358
359 pub fn get_split_tabs(&self, split_id: LeafId) -> Vec<BufferId> {
361 self.split_view_states
362 .get(&split_id)
363 .map(|vs| vs.open_buffers.clone())
364 .unwrap_or_default()
365 }
366
367 pub fn get_split_count(&self) -> usize {
369 self.split_manager.root().count_leaves()
370 }
371
372 pub fn compute_drop_zone(
374 &self,
375 col: u16,
376 row: u16,
377 source_split_id: LeafId,
378 ) -> Option<super::types::TabDropZone> {
379 self.compute_tab_drop_zone(col, row, source_split_id)
380 }
381}