1use rust_i18n::t;
13
14use crate::model::event::{BufferId, Event, LeafId};
15use crate::view::prompt::PromptType;
16
17use super::Editor;
18
19impl Editor {
20 pub fn close_buffer(&mut self, id: BufferId) -> anyhow::Result<()> {
22 if let Some(state) = self
24 .windows
25 .get(&self.active_window)
26 .map(|w| &w.buffers)
27 .expect("active window present")
28 .get(&id)
29 {
30 if state.buffer.is_modified() {
31 return Err(anyhow::anyhow!("Buffer has unsaved changes"));
32 }
33 }
34 self.close_buffer_internal(id)
35 }
36
37 pub fn force_close_buffer(&mut self, id: BufferId) -> anyhow::Result<()> {
40 self.close_buffer_internal(id)
41 }
42
43 fn close_buffer_internal(&mut self, id: BufferId) -> anyhow::Result<()> {
45 if let Some((_, preview_id)) = self.active_window().preview {
48 if preview_id == id {
49 self.active_window_mut().preview = None;
50 }
51 }
52
53 if let Some((wait_id, _)) = self.active_window_mut().wait_tracking.remove(&id) {
55 self.active_window_mut().completed_waits.push(wait_id);
56 }
57
58 self.active_window().save_file_state_on_close(id);
60
61 if let Err(e) = self.delete_buffer_recovery(id) {
63 tracing::debug!("Failed to delete buffer recovery on close: {}", e);
64 }
65
66 if let Some(terminal_id) = self.active_window_mut().terminal_buffers.remove(&id) {
68 self.active_window_mut().terminal_manager.close(terminal_id);
70
71 let backing_file = self
73 .active_window_mut()
74 .terminal_backing_files
75 .remove(&terminal_id);
76 if let Some(ref path) = backing_file {
77 #[allow(clippy::let_underscore_must_use)]
79 let _ = self.authority.filesystem.remove_file(path);
80 }
81 if let Some(log_file) = self
83 .active_window_mut()
84 .terminal_log_files
85 .remove(&terminal_id)
86 {
87 if backing_file.as_ref() != Some(&log_file) {
88 #[allow(clippy::let_underscore_must_use)]
90 let _ = self.authority.filesystem.remove_file(&log_file);
91 }
92 }
93
94 self.active_window_mut().terminal_mode_resume.remove(&id);
96
97 if self.active_window().terminal_mode {
99 self.active_window_mut().terminal_mode = false;
100 self.active_window_mut().key_context =
101 crate::input::keybindings::KeyContext::Normal;
102 }
103 }
104
105 let active_split = self
109 .windows
110 .get(&self.active_window)
111 .and_then(|w| w.buffers.splits())
112 .map(|(mgr, _)| mgr)
113 .expect("active window must have a populated split layout")
114 .active_split();
115
116 let replacement_target: Option<crate::view::split::TabTarget> = self
117 .windows
118 .get(&self.active_window)
119 .and_then(|w| w.buffers.splits())
120 .map(|(_, vs)| vs)
121 .expect("active window must have a populated split layout")
122 .get(&active_split)
123 .and_then(|vs| {
124 use crate::view::split::TabTarget;
125 vs.focus_history.iter().rev().find_map(|t| match t {
126 TabTarget::Buffer(bid) if *bid == id => None, TabTarget::Buffer(bid) => {
128 let hidden = self
130 .active_window()
131 .buffer_metadata
132 .get(bid)
133 .map(|m| m.hidden_from_tabs)
134 .unwrap_or(false);
135 if hidden
136 || !self
137 .windows
138 .get(&self.active_window)
139 .map(|w| &w.buffers)
140 .expect("active window present")
141 .contains_key(bid)
142 {
143 None
144 } else {
145 Some(*t)
146 }
147 }
148 TabTarget::Group(leaf) => {
149 if self.active_window().grouped_subtrees.contains_key(leaf) {
151 Some(*t)
152 } else {
153 None
154 }
155 }
156 })
157 });
158
159 let fallback_buffer: Option<BufferId> = self.buffers().find_id(|bid, _| {
162 bid != id
163 && !self
164 .active_window()
165 .buffer_metadata
166 .get(&bid)
167 .map(|m| m.hidden_from_tabs)
168 .unwrap_or(false)
169 });
170
171 let closing_active = self.active_buffer() == id;
174
175 let return_to_group = match replacement_target {
182 Some(crate::view::split::TabTarget::Group(leaf)) => Some(leaf),
183 _ => None,
184 };
185
186 let direct_replacement = match replacement_target {
187 Some(crate::view::split::TabTarget::Buffer(bid)) => Some(bid),
188 _ => None,
189 };
190
191 let already_keyed = return_to_group.and_then(|_| {
199 self.windows
200 .get(&self.active_window)
201 .and_then(|w| w.buffers.splits())
202 .map(|(_, vs)| vs)
203 .expect("active window must have a populated split layout")
204 .get(&active_split)?
205 .keyed_states
206 .keys()
207 .find(|&&bid| bid != id)
208 .copied()
209 });
210
211 let any_remaining = return_to_group.and_then(|_| {
215 self.windows
216 .get(&self.active_window)
217 .map(|w| &w.buffers)
218 .expect("active window present")
219 .find_id(|bid, _| bid != id)
220 });
221
222 let (replacement_buffer, created_empty_buffer) = match direct_replacement
223 .or(already_keyed)
224 .or(fallback_buffer)
225 .or(any_remaining)
226 {
227 Some(bid) => (bid, false),
228 None => {
229 let new_id = self.new_buffer();
235 if !self
236 .config
237 .editor
238 .auto_create_empty_buffer_on_last_buffer_close
239 {
240 if let Some(meta) = self.active_window_mut().buffer_metadata.get_mut(&new_id) {
241 meta.hidden_from_tabs = true;
242 meta.synthetic_placeholder = true;
243 }
244 }
245 (new_id, true)
246 }
247 };
248
249 if closing_active {
253 self.set_active_buffer(replacement_buffer);
254
255 let hidden = self
264 .active_window()
265 .buffer_metadata
266 .get(&replacement_buffer)
267 .is_some_and(|m| m.hidden_from_tabs);
268 if return_to_group.is_some() && hidden {
269 use crate::view::split::TabTarget;
270 if let Some(vs) = self
271 .windows
272 .get_mut(&self.active_window)
273 .and_then(|w| w.split_view_states_mut())
274 .expect("active window must have a populated split layout")
275 .get_mut(&active_split)
276 {
277 vs.open_buffers
278 .retain(|t| *t != TabTarget::Buffer(replacement_buffer));
279 vs.focus_history
280 .retain(|t| *t != TabTarget::Buffer(replacement_buffer));
281 }
282 }
283 }
284
285 let splits_to_update = self
291 .windows
292 .get(&self.active_window)
293 .and_then(|w| w.buffers.splits())
294 .map(|(mgr, _)| mgr)
295 .expect("active window must have a populated split layout")
296 .splits_for_buffer(id);
297 for split_id in splits_to_update {
298 self.active_window_mut()
299 .set_pane_buffer(split_id, replacement_buffer);
300 }
301
302 self.windows
303 .get_mut(&self.active_window)
304 .map(|w| &mut w.buffers)
305 .expect("active window present")
306 .remove(&id);
307 self.detach_buffer_from_all_windows(id);
308 self.active_window_mut().event_logs.remove(&id);
309 self.active_window_mut().seen_byte_ranges.remove(&id);
310 self.active_window_mut().buffer_metadata.remove(&id);
311 self.active_window_mut().status_bar_values.remove(&id);
312 if let Some((request_id, _, _)) = self
313 .active_window_mut()
314 .semantic_tokens_in_flight
315 .remove(&id)
316 {
317 self.active_window_mut()
318 .pending_semantic_token_requests
319 .remove(&request_id);
320 }
321 if let Some((request_id, _, _, _)) = self
322 .active_window_mut()
323 .semantic_tokens_range_in_flight
324 .remove(&id)
325 {
326 self.active_window_mut()
327 .pending_semantic_token_range_requests
328 .remove(&request_id);
329 }
330 self.active_window_mut()
331 .semantic_tokens_range_last_request
332 .remove(&id);
333 self.active_window_mut()
334 .semantic_tokens_range_applied
335 .remove(&id);
336 self.active_window_mut()
337 .semantic_tokens_full_debounce
338 .remove(&id);
339
340 self.panel_ids_mut().retain(|_, &mut buf_id| buf_id != id);
344
345 for view_state in self
347 .windows
348 .get_mut(&self.active_window)
349 .and_then(|w| w.split_view_states_mut())
350 .expect("active window must have a populated split layout")
351 .values_mut()
352 {
353 view_state.remove_buffer(id);
354 view_state.remove_from_history(id);
355 }
356
357 if closing_active {
358 if created_empty_buffer && self.config.file_explorer.auto_open_on_last_buffer_close {
359 self.focus_file_explorer();
360 }
361 if let Some(group_leaf) = return_to_group {
362 self.activate_group_tab(active_split, group_leaf);
363 }
364 }
365
366 self.plugin_manager.read().unwrap().run_hook(
371 "buffer_closed",
372 fresh_core::hooks::HookArgs::BufferClosed { buffer_id: id },
373 );
374
375 Ok(())
376 }
377
378 pub fn switch_buffer(&mut self, id: BufferId) {
380 if self
381 .windows
382 .get(&self.active_window)
383 .map(|w| &w.buffers)
384 .expect("active window present")
385 .contains_key(&id)
386 && id != self.active_buffer()
387 {
388 self.active_window_mut()
390 .position_history
391 .commit_pending_movement();
392
393 let cursors = self.active_cursors();
395 let position = cursors.primary().position;
396 let anchor = cursors.primary().anchor;
397 let buffer_id = self.active_buffer();
398 let ph = &mut self.active_window_mut().position_history;
399 ph.record_movement(buffer_id, position, anchor);
400 ph.commit_pending_movement();
401
402 self.set_active_buffer(id);
403 }
404 }
405
406 pub fn close_tab(&mut self) {
415 let active_split = self
420 .windows
421 .get(&self.active_window)
422 .and_then(|w| w.buffers.splits())
423 .map(|(mgr, _)| mgr)
424 .expect("active window must have a populated split layout")
425 .active_split();
426 if let Some(group_leaf_id) = self
427 .windows
428 .get(&self.active_window)
429 .and_then(|w| w.buffers.splits())
430 .map(|(_, vs)| vs)
431 .expect("active window must have a populated split layout")
432 .get(&active_split)
433 .and_then(|vs| vs.active_group_tab)
434 {
435 self.close_buffer_group_by_leaf(group_leaf_id);
436 self.set_status_message(t!("buffer.tab_closed").to_string());
437 return;
438 }
439
440 let buffer_id = self.active_buffer();
444 self.close_tab_in_split(buffer_id, active_split);
445 }
446
447 pub fn close_tab_in_split(&mut self, buffer_id: BufferId, split_id: LeafId) -> bool {
457 if self.active_window().terminal_mode && self.active_window().is_terminal_buffer(buffer_id)
459 {
460 self.active_window_mut().terminal_mode = false;
461 self.active_window_mut().key_context = crate::input::keybindings::KeyContext::Normal;
462 }
463
464 let buffer_in_other_splits = self
466 .windows
467 .get(&self.active_window)
468 .and_then(|w| w.buffers.splits())
469 .map(|(_, vs)| vs)
470 .expect("active window must have a populated split layout")
471 .iter()
472 .filter(|(&sid, view_state)| sid != split_id && view_state.has_buffer(buffer_id))
473 .count();
474
475 let split_tabs = self
477 .windows
478 .get(&self.active_window)
479 .and_then(|w| w.buffers.splits())
480 .map(|(_, vs)| vs)
481 .expect("active window must have a populated split layout")
482 .get(&split_id)
483 .map(|vs| vs.buffer_tab_ids_vec())
484 .unwrap_or_default();
485
486 let is_last_viewport = buffer_in_other_splits == 0;
487
488 if is_last_viewport {
489 if let Some(state) = self
491 .windows
492 .get(&self.active_window)
493 .map(|w| &w.buffers)
494 .expect("active window present")
495 .get(&buffer_id)
496 {
497 if state.buffer.is_modified() {
498 let name = self.get_buffer_display_name(buffer_id);
500 let save_key = t!("prompt.key.save").to_string();
501 let discard_key = t!("prompt.key.discard").to_string();
502 let cancel_key = t!("prompt.key.cancel").to_string();
503 self.start_prompt(
504 t!(
505 "prompt.buffer_modified",
506 name = name,
507 save_key = save_key,
508 discard_key = discard_key,
509 cancel_key = cancel_key
510 )
511 .to_string(),
512 PromptType::ConfirmCloseBuffer { buffer_id },
513 );
514 return false;
515 }
516 }
517 let has_other_splits = self
524 .windows
525 .get(&self.active_window)
526 .and_then(|w| w.buffers.splits())
527 .map(|(mgr, _)| mgr)
528 .expect("active window must have a populated split layout")
529 .root()
530 .count_leaves()
531 > 1;
532 if split_tabs.len() <= 1 && has_other_splits {
533 self.handle_close_split(split_id.into());
534 if let Err(e) = self.close_buffer(buffer_id) {
537 tracing::debug!(
538 "close_tab_in_split: buffer cleanup after split close failed: {}",
539 e
540 );
541 }
542 self.set_status_message(t!("buffer.tab_closed").to_string());
543 return true;
544 }
545 if let Err(e) = self.close_buffer(buffer_id) {
546 self.set_status_message(t!("file.cannot_close", error = e.to_string()).to_string());
547 } else {
548 self.set_status_message(t!("buffer.tab_closed").to_string());
549 }
550 } else {
551 use crate::view::split::TabTarget;
552
553 let targets: Vec<TabTarget> = self
560 .windows
561 .get(&self.active_window)
562 .and_then(|w| w.buffers.splits())
563 .map(|(_, vs)| vs)
564 .expect("active window must have a populated split layout")
565 .get(&split_id)
566 .map(|vs| vs.open_buffers.clone())
567 .unwrap_or_default();
568
569 let closing = TabTarget::Buffer(buffer_id);
570 let closing_idx = targets.iter().position(|t| *t == closing).unwrap_or(0);
571 let has_other_tab = targets.iter().any(|t| *t != closing);
572
573 if !has_other_tab {
574 self.handle_close_split(split_id.into());
576 return true;
577 }
578
579 let replacement = if closing_idx > 0 {
584 targets[closing_idx - 1]
585 } else {
586 *targets
588 .iter()
589 .find(|t| **t != closing)
590 .expect("has_other_tab")
591 };
592
593 if let Some(view_state) = self
595 .windows
596 .get_mut(&self.active_window)
597 .and_then(|w| w.split_view_states_mut())
598 .expect("active window must have a populated split layout")
599 .get_mut(&split_id)
600 {
601 view_state.remove_buffer(buffer_id);
602 }
603
604 match replacement {
607 TabTarget::Buffer(replacement_buffer) => {
608 self.windows
609 .get_mut(&self.active_window)
610 .and_then(|w| w.split_manager_mut())
611 .expect("active window must have a populated split layout")
612 .set_split_buffer(split_id, replacement_buffer);
613 }
614 TabTarget::Group(group_leaf) => {
615 self.activate_group_tab(split_id, group_leaf);
616 }
617 }
618
619 self.set_status_message(t!("buffer.tab_closed").to_string());
620 }
621 true
622 }
623
624 pub fn close_other_tabs_in_split(&mut self, keep_buffer_id: BufferId, split_id: LeafId) {
626 let split_tabs = self
628 .windows
629 .get(&self.active_window)
630 .and_then(|w| w.buffers.splits())
631 .map(|(_, vs)| vs)
632 .expect("active window must have a populated split layout")
633 .get(&split_id)
634 .map(|vs| vs.buffer_tab_ids_vec())
635 .unwrap_or_default();
636
637 let tabs_to_close: Vec<_> = split_tabs
639 .iter()
640 .filter(|&&id| id != keep_buffer_id)
641 .copied()
642 .collect();
643
644 let mut closed = 0;
645 let mut skipped_modified = 0;
646 for buffer_id in tabs_to_close {
647 if self.close_tab_in_split_silent(buffer_id, split_id) {
648 closed += 1;
649 } else {
650 skipped_modified += 1;
651 }
652 }
653
654 self.windows
656 .get_mut(&self.active_window)
657 .and_then(|w| w.split_manager_mut())
658 .expect("active window must have a populated split layout")
659 .set_split_buffer(split_id, keep_buffer_id);
660
661 self.set_batch_close_status_message(closed, skipped_modified);
662 }
663
664 pub fn close_tabs_to_right_in_split(&mut self, buffer_id: BufferId, split_id: LeafId) {
666 let split_tabs = self
668 .windows
669 .get(&self.active_window)
670 .and_then(|w| w.buffers.splits())
671 .map(|(_, vs)| vs)
672 .expect("active window must have a populated split layout")
673 .get(&split_id)
674 .map(|vs| vs.buffer_tab_ids_vec())
675 .unwrap_or_default();
676
677 let Some(target_idx) = split_tabs.iter().position(|&id| id == buffer_id) else {
679 return;
680 };
681
682 let tabs_to_close: Vec<_> = split_tabs.iter().skip(target_idx + 1).copied().collect();
684
685 let mut closed = 0;
686 let mut skipped_modified = 0;
687 for buf_id in tabs_to_close {
688 if self.close_tab_in_split_silent(buf_id, split_id) {
689 closed += 1;
690 } else {
691 skipped_modified += 1;
692 }
693 }
694
695 self.set_batch_close_status_message(closed, skipped_modified);
696 }
697
698 pub fn close_tabs_to_left_in_split(&mut self, buffer_id: BufferId, split_id: LeafId) {
700 let split_tabs = self
702 .windows
703 .get(&self.active_window)
704 .and_then(|w| w.buffers.splits())
705 .map(|(_, vs)| vs)
706 .expect("active window must have a populated split layout")
707 .get(&split_id)
708 .map(|vs| vs.buffer_tab_ids_vec())
709 .unwrap_or_default();
710
711 let Some(target_idx) = split_tabs.iter().position(|&id| id == buffer_id) else {
713 return;
714 };
715
716 let tabs_to_close: Vec<_> = split_tabs.iter().take(target_idx).copied().collect();
718
719 let mut closed = 0;
720 let mut skipped_modified = 0;
721 for buf_id in tabs_to_close {
722 if self.close_tab_in_split_silent(buf_id, split_id) {
723 closed += 1;
724 } else {
725 skipped_modified += 1;
726 }
727 }
728
729 self.set_batch_close_status_message(closed, skipped_modified);
730 }
731
732 pub fn close_all_tabs_in_split(&mut self, split_id: LeafId) {
734 let split_tabs = self
736 .windows
737 .get(&self.active_window)
738 .and_then(|w| w.buffers.splits())
739 .map(|(_, vs)| vs)
740 .expect("active window must have a populated split layout")
741 .get(&split_id)
742 .map(|vs| vs.buffer_tab_ids_vec())
743 .unwrap_or_default();
744
745 let mut closed = 0;
746 let mut skipped_modified = 0;
747
748 for buffer_id in split_tabs {
750 if self.close_tab_in_split_silent(buffer_id, split_id) {
751 closed += 1;
752 } else {
753 skipped_modified += 1;
754 }
755 }
756
757 self.set_batch_close_status_message(closed, skipped_modified);
758 }
759
760 fn set_batch_close_status_message(&mut self, closed: usize, skipped_modified: usize) {
762 let message = match (closed, skipped_modified) {
763 (0, 0) => t!("buffer.no_tabs_to_close").to_string(),
764 (0, n) => t!("buffer.skipped_modified", count = n).to_string(),
765 (n, 0) => t!("buffer.closed_tabs", count = n).to_string(),
766 (c, s) => t!("buffer.closed_tabs_skipped", closed = c, skipped = s).to_string(),
767 };
768 self.set_status_message(message);
769 }
770
771 fn close_tab_in_split_silent(&mut self, buffer_id: BufferId, split_id: LeafId) -> bool {
775 if self.active_window().terminal_mode && self.active_window().is_terminal_buffer(buffer_id)
777 {
778 self.active_window_mut().terminal_mode = false;
779 self.active_window_mut().key_context = crate::input::keybindings::KeyContext::Normal;
780 }
781
782 let buffer_in_other_splits = self
784 .windows
785 .get(&self.active_window)
786 .and_then(|w| w.buffers.splits())
787 .map(|(_, vs)| vs)
788 .expect("active window must have a populated split layout")
789 .iter()
790 .filter(|(&sid, view_state)| sid != split_id && view_state.has_buffer(buffer_id))
791 .count();
792
793 let split_tabs = self
795 .windows
796 .get(&self.active_window)
797 .and_then(|w| w.buffers.splits())
798 .map(|(_, vs)| vs)
799 .expect("active window must have a populated split layout")
800 .get(&split_id)
801 .map(|vs| vs.buffer_tab_ids_vec())
802 .unwrap_or_default();
803
804 let is_last_viewport = buffer_in_other_splits == 0;
805
806 if is_last_viewport {
807 if let Some(state) = self
810 .windows
811 .get(&self.active_window)
812 .map(|w| &w.buffers)
813 .expect("active window present")
814 .get(&buffer_id)
815 {
816 if state.buffer.is_modified() {
817 return false;
819 }
820 }
821 if let Err(e) = self.close_buffer(buffer_id) {
822 tracing::warn!("Failed to close buffer: {}", e);
823 }
824 true
825 } else {
826 if split_tabs.len() <= 1 {
828 self.handle_close_split(split_id.into());
830 return true;
831 }
832
833 let current_idx = split_tabs
835 .iter()
836 .position(|&id| id == buffer_id)
837 .unwrap_or(0);
838 let replacement_idx = if current_idx > 0 { current_idx - 1 } else { 1 };
839 let replacement_buffer = split_tabs.get(replacement_idx).copied();
840
841 if let Some(view_state) = self
843 .windows
844 .get_mut(&self.active_window)
845 .and_then(|w| w.split_view_states_mut())
846 .expect("active window must have a populated split layout")
847 .get_mut(&split_id)
848 {
849 view_state.remove_buffer(buffer_id);
850 }
851
852 if let Some(replacement) = replacement_buffer {
855 self.active_window_mut()
856 .set_pane_buffer(split_id, replacement);
857 }
858 true
859 }
860 }
861
862 pub fn next_buffer(&mut self) {
864 self.cycle_tab(1);
865 }
866
867 pub fn prev_buffer(&mut self) {
869 self.cycle_tab(-1);
870 }
871
872 fn cycle_tab(&mut self, direction: i32) {
875 use crate::view::split::TabTarget;
876
877 let active_split = self
878 .windows
879 .get(&self.active_window)
880 .and_then(|w| w.buffers.splits())
881 .map(|(mgr, _)| mgr)
882 .expect("active window must have a populated split layout")
883 .active_split();
884 let Some(view_state) = self
885 .windows
886 .get(&self.active_window)
887 .and_then(|w| w.buffers.splits())
888 .map(|(_, vs)| vs)
889 .expect("active window must have a populated split layout")
890 .get(&active_split)
891 else {
892 return;
893 };
894
895 let targets: Vec<TabTarget> = view_state
897 .open_buffers
898 .iter()
899 .copied()
900 .filter(|t| match t {
901 TabTarget::Buffer(id) => !self
902 .active_window()
903 .buffer_metadata
904 .get(id)
905 .map(|m| m.hidden_from_tabs)
906 .unwrap_or(false),
907 TabTarget::Group(_) => true,
908 })
909 .collect();
910
911 if targets.len() < 2 {
912 return;
913 }
914
915 let current_target = view_state.active_target();
916 let Some(idx) = targets.iter().position(|t| *t == current_target) else {
917 return;
918 };
919
920 let next_idx = if direction > 0 {
921 (idx + 1) % targets.len()
922 } else if idx == 0 {
923 targets.len() - 1
924 } else {
925 idx - 1
926 };
927
928 if targets[next_idx] == current_target {
929 return;
930 }
931
932 self.active_window_mut()
934 .position_history
935 .commit_pending_movement();
936 let cursors = self.active_cursors();
937 let position = cursors.primary().position;
938 let anchor = cursors.primary().anchor;
939 let buffer_id = self.active_buffer();
940 let ph = &mut self.active_window_mut().position_history;
941 ph.record_movement(buffer_id, position, anchor);
942 ph.commit_pending_movement();
943
944 self.active_window_mut()
952 .animate_tab_switch(active_split, direction.signum());
953
954 match targets[next_idx] {
955 TabTarget::Buffer(buffer_id) => {
956 self.set_active_buffer(buffer_id);
957 }
958 TabTarget::Group(group_leaf_id) => {
959 self.activate_group_tab(active_split, group_leaf_id);
960 }
961 }
962 }
963
964 pub fn navigate_back(&mut self) {
966 self.active_window_mut().in_navigation = true;
968
969 self.active_window_mut()
971 .position_history
972 .commit_pending_movement();
973
974 if self.active_window_mut().position_history.can_go_back()
977 && !self.active_window_mut().position_history.can_go_forward()
978 {
979 let cursors = self.active_cursors();
980 let position = cursors.primary().position;
981 let anchor = cursors.primary().anchor;
982 let buffer_id = self.active_buffer();
983 let ph = &mut self.active_window_mut().position_history;
984 ph.record_movement(buffer_id, position, anchor);
985 ph.commit_pending_movement();
986 }
987
988 if let Some(entry) = self.active_window_mut().position_history.back() {
990 let target_buffer = entry.buffer_id;
991 let target_position = entry.position;
992 let target_anchor = entry.anchor;
993
994 if self
996 .windows
997 .get(&self.active_window)
998 .map(|w| &w.buffers)
999 .expect("active window present")
1000 .contains_key(&target_buffer)
1001 {
1002 self.set_active_buffer(target_buffer);
1003
1004 let cursors = self.active_cursors();
1006 let cursor_id = cursors.primary_id();
1007 let old_position = cursors.primary().position;
1008 let old_anchor = cursors.primary().anchor;
1009 let old_sticky_column = cursors.primary().sticky_column;
1010 let event = Event::MoveCursor {
1011 cursor_id,
1012 old_position,
1013 new_position: target_position,
1014 old_anchor,
1015 new_anchor: target_anchor,
1016 old_sticky_column,
1017 new_sticky_column: 0, };
1019 let split_id = self
1020 .windows
1021 .get(&self.active_window)
1022 .and_then(|w| w.buffers.splits())
1023 .map(|(mgr, _)| mgr)
1024 .expect("active window must have a populated split layout")
1025 .active_split();
1026 self.active_window_mut()
1027 .apply_event_to_buffer(target_buffer, split_id, &event);
1028 self.active_window_mut()
1032 .ensure_active_cursor_visible_for_navigation(true);
1033 }
1034 }
1035
1036 self.active_window_mut().in_navigation = false;
1038 }
1039
1040 pub fn navigate_forward(&mut self) {
1042 self.active_window_mut().in_navigation = true;
1044
1045 if let Some(entry) = self.active_window_mut().position_history.forward() {
1046 let target_buffer = entry.buffer_id;
1047 let target_position = entry.position;
1048 let target_anchor = entry.anchor;
1049
1050 if self
1052 .windows
1053 .get(&self.active_window)
1054 .map(|w| &w.buffers)
1055 .expect("active window present")
1056 .contains_key(&target_buffer)
1057 {
1058 self.set_active_buffer(target_buffer);
1059
1060 let cursors = self.active_cursors();
1062 let cursor_id = cursors.primary_id();
1063 let old_position = cursors.primary().position;
1064 let old_anchor = cursors.primary().anchor;
1065 let old_sticky_column = cursors.primary().sticky_column;
1066 let event = Event::MoveCursor {
1067 cursor_id,
1068 old_position,
1069 new_position: target_position,
1070 old_anchor,
1071 new_anchor: target_anchor,
1072 old_sticky_column,
1073 new_sticky_column: 0, };
1075 let split_id = self
1076 .windows
1077 .get(&self.active_window)
1078 .and_then(|w| w.buffers.splits())
1079 .map(|(mgr, _)| mgr)
1080 .expect("active window must have a populated split layout")
1081 .active_split();
1082 self.active_window_mut()
1083 .apply_event_to_buffer(target_buffer, split_id, &event);
1084 self.active_window_mut()
1088 .ensure_active_cursor_visible_for_navigation(true);
1089 }
1090 }
1091
1092 self.active_window_mut().in_navigation = false;
1094 }
1095}