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 if split_tabs.len() <= 1 {
553 self.handle_close_split(split_id.into());
555 return true;
556 }
557
558 let current_idx = split_tabs
560 .iter()
561 .position(|&id| id == buffer_id)
562 .unwrap_or(0);
563 let replacement_idx = if current_idx > 0 { current_idx - 1 } else { 1 };
564 let replacement_buffer = split_tabs[replacement_idx];
565
566 if let Some(view_state) = self
568 .windows
569 .get_mut(&self.active_window)
570 .and_then(|w| w.split_view_states_mut())
571 .expect("active window must have a populated split layout")
572 .get_mut(&split_id)
573 {
574 view_state.remove_buffer(buffer_id);
575 }
576
577 self.windows
579 .get_mut(&self.active_window)
580 .and_then(|w| w.split_manager_mut())
581 .expect("active window must have a populated split layout")
582 .set_split_buffer(split_id, replacement_buffer);
583
584 self.set_status_message(t!("buffer.tab_closed").to_string());
585 }
586 true
587 }
588
589 pub fn close_other_tabs_in_split(&mut self, keep_buffer_id: BufferId, split_id: LeafId) {
591 let split_tabs = self
593 .windows
594 .get(&self.active_window)
595 .and_then(|w| w.buffers.splits())
596 .map(|(_, vs)| vs)
597 .expect("active window must have a populated split layout")
598 .get(&split_id)
599 .map(|vs| vs.buffer_tab_ids_vec())
600 .unwrap_or_default();
601
602 let tabs_to_close: Vec<_> = split_tabs
604 .iter()
605 .filter(|&&id| id != keep_buffer_id)
606 .copied()
607 .collect();
608
609 let mut closed = 0;
610 let mut skipped_modified = 0;
611 for buffer_id in tabs_to_close {
612 if self.close_tab_in_split_silent(buffer_id, split_id) {
613 closed += 1;
614 } else {
615 skipped_modified += 1;
616 }
617 }
618
619 self.windows
621 .get_mut(&self.active_window)
622 .and_then(|w| w.split_manager_mut())
623 .expect("active window must have a populated split layout")
624 .set_split_buffer(split_id, keep_buffer_id);
625
626 self.set_batch_close_status_message(closed, skipped_modified);
627 }
628
629 pub fn close_tabs_to_right_in_split(&mut self, buffer_id: BufferId, split_id: LeafId) {
631 let split_tabs = self
633 .windows
634 .get(&self.active_window)
635 .and_then(|w| w.buffers.splits())
636 .map(|(_, vs)| vs)
637 .expect("active window must have a populated split layout")
638 .get(&split_id)
639 .map(|vs| vs.buffer_tab_ids_vec())
640 .unwrap_or_default();
641
642 let Some(target_idx) = split_tabs.iter().position(|&id| id == buffer_id) else {
644 return;
645 };
646
647 let tabs_to_close: Vec<_> = split_tabs.iter().skip(target_idx + 1).copied().collect();
649
650 let mut closed = 0;
651 let mut skipped_modified = 0;
652 for buf_id in tabs_to_close {
653 if self.close_tab_in_split_silent(buf_id, split_id) {
654 closed += 1;
655 } else {
656 skipped_modified += 1;
657 }
658 }
659
660 self.set_batch_close_status_message(closed, skipped_modified);
661 }
662
663 pub fn close_tabs_to_left_in_split(&mut self, buffer_id: BufferId, split_id: LeafId) {
665 let split_tabs = self
667 .windows
668 .get(&self.active_window)
669 .and_then(|w| w.buffers.splits())
670 .map(|(_, vs)| vs)
671 .expect("active window must have a populated split layout")
672 .get(&split_id)
673 .map(|vs| vs.buffer_tab_ids_vec())
674 .unwrap_or_default();
675
676 let Some(target_idx) = split_tabs.iter().position(|&id| id == buffer_id) else {
678 return;
679 };
680
681 let tabs_to_close: Vec<_> = split_tabs.iter().take(target_idx).copied().collect();
683
684 let mut closed = 0;
685 let mut skipped_modified = 0;
686 for buf_id in tabs_to_close {
687 if self.close_tab_in_split_silent(buf_id, split_id) {
688 closed += 1;
689 } else {
690 skipped_modified += 1;
691 }
692 }
693
694 self.set_batch_close_status_message(closed, skipped_modified);
695 }
696
697 pub fn close_all_tabs_in_split(&mut self, split_id: LeafId) {
699 let split_tabs = self
701 .windows
702 .get(&self.active_window)
703 .and_then(|w| w.buffers.splits())
704 .map(|(_, vs)| vs)
705 .expect("active window must have a populated split layout")
706 .get(&split_id)
707 .map(|vs| vs.buffer_tab_ids_vec())
708 .unwrap_or_default();
709
710 let mut closed = 0;
711 let mut skipped_modified = 0;
712
713 for buffer_id in split_tabs {
715 if self.close_tab_in_split_silent(buffer_id, split_id) {
716 closed += 1;
717 } else {
718 skipped_modified += 1;
719 }
720 }
721
722 self.set_batch_close_status_message(closed, skipped_modified);
723 }
724
725 fn set_batch_close_status_message(&mut self, closed: usize, skipped_modified: usize) {
727 let message = match (closed, skipped_modified) {
728 (0, 0) => t!("buffer.no_tabs_to_close").to_string(),
729 (0, n) => t!("buffer.skipped_modified", count = n).to_string(),
730 (n, 0) => t!("buffer.closed_tabs", count = n).to_string(),
731 (c, s) => t!("buffer.closed_tabs_skipped", closed = c, skipped = s).to_string(),
732 };
733 self.set_status_message(message);
734 }
735
736 fn close_tab_in_split_silent(&mut self, buffer_id: BufferId, split_id: LeafId) -> bool {
740 if self.active_window().terminal_mode && self.active_window().is_terminal_buffer(buffer_id)
742 {
743 self.active_window_mut().terminal_mode = false;
744 self.active_window_mut().key_context = crate::input::keybindings::KeyContext::Normal;
745 }
746
747 let buffer_in_other_splits = self
749 .windows
750 .get(&self.active_window)
751 .and_then(|w| w.buffers.splits())
752 .map(|(_, vs)| vs)
753 .expect("active window must have a populated split layout")
754 .iter()
755 .filter(|(&sid, view_state)| sid != split_id && view_state.has_buffer(buffer_id))
756 .count();
757
758 let split_tabs = self
760 .windows
761 .get(&self.active_window)
762 .and_then(|w| w.buffers.splits())
763 .map(|(_, vs)| vs)
764 .expect("active window must have a populated split layout")
765 .get(&split_id)
766 .map(|vs| vs.buffer_tab_ids_vec())
767 .unwrap_or_default();
768
769 let is_last_viewport = buffer_in_other_splits == 0;
770
771 if is_last_viewport {
772 if let Some(state) = self
775 .windows
776 .get(&self.active_window)
777 .map(|w| &w.buffers)
778 .expect("active window present")
779 .get(&buffer_id)
780 {
781 if state.buffer.is_modified() {
782 return false;
784 }
785 }
786 if let Err(e) = self.close_buffer(buffer_id) {
787 tracing::warn!("Failed to close buffer: {}", e);
788 }
789 true
790 } else {
791 if split_tabs.len() <= 1 {
793 self.handle_close_split(split_id.into());
795 return true;
796 }
797
798 let current_idx = split_tabs
800 .iter()
801 .position(|&id| id == buffer_id)
802 .unwrap_or(0);
803 let replacement_idx = if current_idx > 0 { current_idx - 1 } else { 1 };
804 let replacement_buffer = split_tabs.get(replacement_idx).copied();
805
806 if let Some(view_state) = self
808 .windows
809 .get_mut(&self.active_window)
810 .and_then(|w| w.split_view_states_mut())
811 .expect("active window must have a populated split layout")
812 .get_mut(&split_id)
813 {
814 view_state.remove_buffer(buffer_id);
815 }
816
817 if let Some(replacement) = replacement_buffer {
820 self.active_window_mut()
821 .set_pane_buffer(split_id, replacement);
822 }
823 true
824 }
825 }
826
827 pub fn next_buffer(&mut self) {
829 self.cycle_tab(1);
830 }
831
832 pub fn prev_buffer(&mut self) {
834 self.cycle_tab(-1);
835 }
836
837 fn cycle_tab(&mut self, direction: i32) {
840 use crate::view::split::TabTarget;
841
842 let active_split = self
843 .windows
844 .get(&self.active_window)
845 .and_then(|w| w.buffers.splits())
846 .map(|(mgr, _)| mgr)
847 .expect("active window must have a populated split layout")
848 .active_split();
849 let Some(view_state) = self
850 .windows
851 .get(&self.active_window)
852 .and_then(|w| w.buffers.splits())
853 .map(|(_, vs)| vs)
854 .expect("active window must have a populated split layout")
855 .get(&active_split)
856 else {
857 return;
858 };
859
860 let targets: Vec<TabTarget> = view_state
862 .open_buffers
863 .iter()
864 .copied()
865 .filter(|t| match t {
866 TabTarget::Buffer(id) => !self
867 .active_window()
868 .buffer_metadata
869 .get(id)
870 .map(|m| m.hidden_from_tabs)
871 .unwrap_or(false),
872 TabTarget::Group(_) => true,
873 })
874 .collect();
875
876 if targets.len() < 2 {
877 return;
878 }
879
880 let current_target = view_state.active_target();
881 let Some(idx) = targets.iter().position(|t| *t == current_target) else {
882 return;
883 };
884
885 let next_idx = if direction > 0 {
886 (idx + 1) % targets.len()
887 } else if idx == 0 {
888 targets.len() - 1
889 } else {
890 idx - 1
891 };
892
893 if targets[next_idx] == current_target {
894 return;
895 }
896
897 self.active_window_mut()
899 .position_history
900 .commit_pending_movement();
901 let cursors = self.active_cursors();
902 let position = cursors.primary().position;
903 let anchor = cursors.primary().anchor;
904 let buffer_id = self.active_buffer();
905 let ph = &mut self.active_window_mut().position_history;
906 ph.record_movement(buffer_id, position, anchor);
907 ph.commit_pending_movement();
908
909 self.active_window_mut()
917 .animate_tab_switch(active_split, direction.signum());
918
919 match targets[next_idx] {
920 TabTarget::Buffer(buffer_id) => {
921 self.set_active_buffer(buffer_id);
922 }
923 TabTarget::Group(group_leaf_id) => {
924 self.activate_group_tab(active_split, group_leaf_id);
925 }
926 }
927 }
928
929 pub fn navigate_back(&mut self) {
931 self.active_window_mut().in_navigation = true;
933
934 self.active_window_mut()
936 .position_history
937 .commit_pending_movement();
938
939 if self.active_window_mut().position_history.can_go_back()
942 && !self.active_window_mut().position_history.can_go_forward()
943 {
944 let cursors = self.active_cursors();
945 let position = cursors.primary().position;
946 let anchor = cursors.primary().anchor;
947 let buffer_id = self.active_buffer();
948 let ph = &mut self.active_window_mut().position_history;
949 ph.record_movement(buffer_id, position, anchor);
950 ph.commit_pending_movement();
951 }
952
953 if let Some(entry) = self.active_window_mut().position_history.back() {
955 let target_buffer = entry.buffer_id;
956 let target_position = entry.position;
957 let target_anchor = entry.anchor;
958
959 if self
961 .windows
962 .get(&self.active_window)
963 .map(|w| &w.buffers)
964 .expect("active window present")
965 .contains_key(&target_buffer)
966 {
967 self.set_active_buffer(target_buffer);
968
969 let cursors = self.active_cursors();
971 let cursor_id = cursors.primary_id();
972 let old_position = cursors.primary().position;
973 let old_anchor = cursors.primary().anchor;
974 let old_sticky_column = cursors.primary().sticky_column;
975 let event = Event::MoveCursor {
976 cursor_id,
977 old_position,
978 new_position: target_position,
979 old_anchor,
980 new_anchor: target_anchor,
981 old_sticky_column,
982 new_sticky_column: 0, };
984 let split_id = self
985 .windows
986 .get(&self.active_window)
987 .and_then(|w| w.buffers.splits())
988 .map(|(mgr, _)| mgr)
989 .expect("active window must have a populated split layout")
990 .active_split();
991 self.active_window_mut()
992 .apply_event_to_buffer(target_buffer, split_id, &event);
993 self.active_window_mut()
997 .ensure_active_cursor_visible_for_navigation(true);
998 }
999 }
1000
1001 self.active_window_mut().in_navigation = false;
1003 }
1004
1005 pub fn navigate_forward(&mut self) {
1007 self.active_window_mut().in_navigation = true;
1009
1010 if let Some(entry) = self.active_window_mut().position_history.forward() {
1011 let target_buffer = entry.buffer_id;
1012 let target_position = entry.position;
1013 let target_anchor = entry.anchor;
1014
1015 if self
1017 .windows
1018 .get(&self.active_window)
1019 .map(|w| &w.buffers)
1020 .expect("active window present")
1021 .contains_key(&target_buffer)
1022 {
1023 self.set_active_buffer(target_buffer);
1024
1025 let cursors = self.active_cursors();
1027 let cursor_id = cursors.primary_id();
1028 let old_position = cursors.primary().position;
1029 let old_anchor = cursors.primary().anchor;
1030 let old_sticky_column = cursors.primary().sticky_column;
1031 let event = Event::MoveCursor {
1032 cursor_id,
1033 old_position,
1034 new_position: target_position,
1035 old_anchor,
1036 new_anchor: target_anchor,
1037 old_sticky_column,
1038 new_sticky_column: 0, };
1040 let split_id = self
1041 .windows
1042 .get(&self.active_window)
1043 .and_then(|w| w.buffers.splits())
1044 .map(|(mgr, _)| mgr)
1045 .expect("active window must have a populated split layout")
1046 .active_split();
1047 self.active_window_mut()
1048 .apply_event_to_buffer(target_buffer, split_id, &event);
1049 self.active_window_mut()
1053 .ensure_active_cursor_visible_for_navigation(true);
1054 }
1055 }
1056
1057 self.active_window_mut().in_navigation = false;
1059 }
1060}