Skip to repository content2549 lines · 93.5 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T02:32:18.946Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitBrowse files
navigation.rs
1use super::*;
2
3impl Editor {
4 pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
5 self.change_selections(Default::default(), window, cx, |s| {
6 s.move_with(&mut |map, selection| {
7 let cursor = if selection.is_empty() {
8 movement::left(map, selection.start)
9 } else {
10 selection.start
11 };
12 selection.collapse_to(cursor, SelectionGoal::None);
13 });
14 })
15 }
16
17 pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
18 self.change_selections(Default::default(), window, cx, |s| {
19 s.move_heads_with(&mut |map, head, _| (movement::left(map, head), SelectionGoal::None));
20 })
21 }
22
23 pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
24 self.change_selections(Default::default(), window, cx, |s| {
25 s.move_with(&mut |map, selection| {
26 let cursor = if selection.is_empty() {
27 movement::right(map, selection.end)
28 } else {
29 selection.end
30 };
31 selection.collapse_to(cursor, SelectionGoal::None)
32 });
33 })
34 }
35
36 pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
37 self.change_selections(Default::default(), window, cx, |s| {
38 s.move_heads_with(&mut |map, head, _| {
39 (movement::right(map, head), SelectionGoal::None)
40 });
41 });
42 }
43
44 pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
45 if self.take_rename(true, window, cx).is_some() {
46 return;
47 }
48
49 if self.mode.is_single_line() {
50 cx.propagate();
51 return;
52 }
53
54 let text_layout_details = &self.text_layout_details(window, cx);
55 let selection_count = self.selections.count();
56 let first_selection = self.selections.first_anchor();
57
58 self.change_selections(Default::default(), window, cx, |s| {
59 s.move_with(&mut |map, selection| {
60 if !selection.is_empty() {
61 selection.goal = SelectionGoal::None;
62 }
63 let (cursor, goal) = movement::up(
64 map,
65 selection.start,
66 selection.goal,
67 false,
68 text_layout_details,
69 );
70 selection.collapse_to(cursor, goal);
71 });
72 });
73
74 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
75 {
76 cx.propagate();
77 }
78 }
79
80 pub fn move_up_by_lines(
81 &mut self,
82 action: &MoveUpByLines,
83 window: &mut Window,
84 cx: &mut Context<Self>,
85 ) {
86 if self.take_rename(true, window, cx).is_some() {
87 return;
88 }
89
90 if self.mode.is_single_line() {
91 cx.propagate();
92 return;
93 }
94
95 let text_layout_details = &self.text_layout_details(window, cx);
96
97 self.change_selections(Default::default(), window, cx, |s| {
98 s.move_with(&mut |map, selection| {
99 if !selection.is_empty() {
100 selection.goal = SelectionGoal::None;
101 }
102 let (cursor, goal) = movement::up_by_rows(
103 map,
104 selection.start,
105 action.lines,
106 selection.goal,
107 false,
108 text_layout_details,
109 );
110 selection.collapse_to(cursor, goal);
111 });
112 })
113 }
114
115 pub fn move_down_by_lines(
116 &mut self,
117 action: &MoveDownByLines,
118 window: &mut Window,
119 cx: &mut Context<Self>,
120 ) {
121 if self.take_rename(true, window, cx).is_some() {
122 return;
123 }
124
125 if self.mode.is_single_line() {
126 cx.propagate();
127 return;
128 }
129
130 let text_layout_details = &self.text_layout_details(window, cx);
131
132 self.change_selections(Default::default(), window, cx, |s| {
133 s.move_with(&mut |map, selection| {
134 if !selection.is_empty() {
135 selection.goal = SelectionGoal::None;
136 }
137 let (cursor, goal) = movement::down_by_rows(
138 map,
139 selection.start,
140 action.lines,
141 selection.goal,
142 false,
143 text_layout_details,
144 );
145 selection.collapse_to(cursor, goal);
146 });
147 })
148 }
149
150 pub fn select_down_by_lines(
151 &mut self,
152 action: &SelectDownByLines,
153 window: &mut Window,
154 cx: &mut Context<Self>,
155 ) {
156 let text_layout_details = &self.text_layout_details(window, cx);
157 self.change_selections(Default::default(), window, cx, |s| {
158 s.move_heads_with(&mut |map, head, goal| {
159 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
160 })
161 })
162 }
163
164 pub fn select_up_by_lines(
165 &mut self,
166 action: &SelectUpByLines,
167 window: &mut Window,
168 cx: &mut Context<Self>,
169 ) {
170 let text_layout_details = &self.text_layout_details(window, cx);
171 self.change_selections(Default::default(), window, cx, |s| {
172 s.move_heads_with(&mut |map, head, goal| {
173 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
174 })
175 })
176 }
177
178 pub fn select_page_up(
179 &mut self,
180 _: &SelectPageUp,
181 window: &mut Window,
182 cx: &mut Context<Self>,
183 ) {
184 let Some(row_count) = self.visible_row_count() else {
185 return;
186 };
187
188 let text_layout_details = &self.text_layout_details(window, cx);
189
190 self.change_selections(Default::default(), window, cx, |s| {
191 s.move_heads_with(&mut |map, head, goal| {
192 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
193 })
194 })
195 }
196
197 pub fn move_page_up(
198 &mut self,
199 action: &MovePageUp,
200 window: &mut Window,
201 cx: &mut Context<Self>,
202 ) {
203 if self.take_rename(true, window, cx).is_some() {
204 return;
205 }
206
207 if self
208 .context_menu
209 .borrow_mut()
210 .as_mut()
211 .map(|menu| menu.select_first(self.completion_provider.as_deref(), window, cx))
212 .unwrap_or(false)
213 {
214 return;
215 }
216
217 if matches!(self.mode, EditorMode::SingleLine) {
218 cx.propagate();
219 return;
220 }
221
222 let Some(row_count) = self.visible_row_count() else {
223 return;
224 };
225
226 let effects = if action.center_cursor {
227 SelectionEffects::scroll(Autoscroll::center())
228 } else {
229 SelectionEffects::default()
230 };
231
232 let text_layout_details = &self.text_layout_details(window, cx);
233
234 self.change_selections(effects, window, cx, |s| {
235 s.move_with(&mut |map, selection| {
236 if !selection.is_empty() {
237 selection.goal = SelectionGoal::None;
238 }
239 let (cursor, goal) = movement::up_by_rows(
240 map,
241 selection.end,
242 row_count,
243 selection.goal,
244 false,
245 text_layout_details,
246 );
247 selection.collapse_to(cursor, goal);
248 });
249 });
250 }
251
252 pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
253 let text_layout_details = &self.text_layout_details(window, cx);
254 self.change_selections(Default::default(), window, cx, |s| {
255 s.move_heads_with(&mut |map, head, goal| {
256 movement::up(map, head, goal, false, text_layout_details)
257 })
258 })
259 }
260
261 pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
262 if self.take_rename(true, window, cx).is_some() {
263 return;
264 }
265
266 if self.mode.is_single_line() {
267 cx.propagate();
268 return;
269 }
270
271 let text_layout_details = &self.text_layout_details(window, cx);
272 let selection_count = self.selections.count();
273 let first_selection = self.selections.first_anchor();
274
275 self.change_selections(Default::default(), window, cx, |s| {
276 s.move_with(&mut |map, selection| {
277 if !selection.is_empty() {
278 selection.goal = SelectionGoal::None;
279 }
280 let (cursor, goal) = movement::down(
281 map,
282 selection.end,
283 selection.goal,
284 false,
285 text_layout_details,
286 );
287 selection.collapse_to(cursor, goal);
288 });
289 });
290
291 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
292 {
293 cx.propagate();
294 }
295 }
296
297 pub fn select_page_down(
298 &mut self,
299 _: &SelectPageDown,
300 window: &mut Window,
301 cx: &mut Context<Self>,
302 ) {
303 let Some(row_count) = self.visible_row_count() else {
304 return;
305 };
306
307 let text_layout_details = &self.text_layout_details(window, cx);
308
309 self.change_selections(Default::default(), window, cx, |s| {
310 s.move_heads_with(&mut |map, head, goal| {
311 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
312 })
313 })
314 }
315
316 pub fn move_page_down(
317 &mut self,
318 action: &MovePageDown,
319 window: &mut Window,
320 cx: &mut Context<Self>,
321 ) {
322 if self.take_rename(true, window, cx).is_some() {
323 return;
324 }
325
326 if self
327 .context_menu
328 .borrow_mut()
329 .as_mut()
330 .map(|menu| menu.select_last(self.completion_provider.as_deref(), window, cx))
331 .unwrap_or(false)
332 {
333 return;
334 }
335
336 if matches!(self.mode, EditorMode::SingleLine) {
337 cx.propagate();
338 return;
339 }
340
341 let Some(row_count) = self.visible_row_count() else {
342 return;
343 };
344
345 let effects = if action.center_cursor {
346 SelectionEffects::scroll(Autoscroll::center())
347 } else {
348 SelectionEffects::default()
349 };
350
351 let text_layout_details = &self.text_layout_details(window, cx);
352 self.change_selections(effects, window, cx, |s| {
353 s.move_with(&mut |map, selection| {
354 if !selection.is_empty() {
355 selection.goal = SelectionGoal::None;
356 }
357 let (cursor, goal) = movement::down_by_rows(
358 map,
359 selection.end,
360 row_count,
361 selection.goal,
362 false,
363 text_layout_details,
364 );
365 selection.collapse_to(cursor, goal);
366 });
367 });
368 }
369
370 pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
371 let text_layout_details = &self.text_layout_details(window, cx);
372 self.change_selections(Default::default(), window, cx, |s| {
373 s.move_heads_with(&mut |map, head, goal| {
374 movement::down(map, head, goal, false, text_layout_details)
375 })
376 });
377 }
378
379 pub fn move_to_previous_word_start(
380 &mut self,
381 _: &MoveToPreviousWordStart,
382 window: &mut Window,
383 cx: &mut Context<Self>,
384 ) {
385 self.change_selections(Default::default(), window, cx, |s| {
386 s.move_cursors_with(&mut |map, head, _| {
387 (
388 movement::previous_word_start(map, head),
389 SelectionGoal::None,
390 )
391 });
392 })
393 }
394
395 pub fn move_to_previous_subword_start(
396 &mut self,
397 _: &MoveToPreviousSubwordStart,
398 window: &mut Window,
399 cx: &mut Context<Self>,
400 ) {
401 self.change_selections(Default::default(), window, cx, |s| {
402 s.move_cursors_with(&mut |map, head, _| {
403 (
404 movement::previous_subword_start(map, head),
405 SelectionGoal::None,
406 )
407 });
408 })
409 }
410
411 pub fn select_to_previous_word_start(
412 &mut self,
413 _: &SelectToPreviousWordStart,
414 window: &mut Window,
415 cx: &mut Context<Self>,
416 ) {
417 self.change_selections(Default::default(), window, cx, |s| {
418 s.move_heads_with(&mut |map, head, _| {
419 (
420 movement::previous_word_start(map, head),
421 SelectionGoal::None,
422 )
423 });
424 })
425 }
426
427 pub fn select_to_previous_subword_start(
428 &mut self,
429 _: &SelectToPreviousSubwordStart,
430 window: &mut Window,
431 cx: &mut Context<Self>,
432 ) {
433 self.change_selections(Default::default(), window, cx, |s| {
434 s.move_heads_with(&mut |map, head, _| {
435 (
436 movement::previous_subword_start(map, head),
437 SelectionGoal::None,
438 )
439 });
440 })
441 }
442
443 pub fn move_to_next_word_end(
444 &mut self,
445 _: &MoveToNextWordEnd,
446 window: &mut Window,
447 cx: &mut Context<Self>,
448 ) {
449 self.change_selections(Default::default(), window, cx, |s| {
450 s.move_cursors_with(&mut |map, head, _| {
451 (movement::next_word_end(map, head), SelectionGoal::None)
452 });
453 })
454 }
455
456 pub fn move_to_next_subword_end(
457 &mut self,
458 _: &MoveToNextSubwordEnd,
459 window: &mut Window,
460 cx: &mut Context<Self>,
461 ) {
462 self.change_selections(Default::default(), window, cx, |s| {
463 s.move_cursors_with(&mut |map, head, _| {
464 (movement::next_subword_end(map, head), SelectionGoal::None)
465 });
466 })
467 }
468
469 pub fn select_to_next_word_end(
470 &mut self,
471 _: &SelectToNextWordEnd,
472 window: &mut Window,
473 cx: &mut Context<Self>,
474 ) {
475 self.change_selections(Default::default(), window, cx, |s| {
476 s.move_heads_with(&mut |map, head, _| {
477 (movement::next_word_end(map, head), SelectionGoal::None)
478 });
479 })
480 }
481
482 pub fn select_to_next_subword_end(
483 &mut self,
484 _: &SelectToNextSubwordEnd,
485 window: &mut Window,
486 cx: &mut Context<Self>,
487 ) {
488 self.change_selections(Default::default(), window, cx, |s| {
489 s.move_heads_with(&mut |map, head, _| {
490 (movement::next_subword_end(map, head), SelectionGoal::None)
491 });
492 })
493 }
494
495 pub fn move_to_beginning_of_line(
496 &mut self,
497 action: &MoveToBeginningOfLine,
498 window: &mut Window,
499 cx: &mut Context<Self>,
500 ) {
501 let stop_at_indent = action.stop_at_indent && !self.mode.is_single_line();
502 self.change_selections(Default::default(), window, cx, |s| {
503 s.move_cursors_with(&mut |map, head, _| {
504 (
505 movement::indented_line_beginning(
506 map,
507 head,
508 action.stop_at_soft_wraps,
509 stop_at_indent,
510 ),
511 SelectionGoal::None,
512 )
513 });
514 })
515 }
516
517 pub fn select_to_beginning_of_line(
518 &mut self,
519 action: &SelectToBeginningOfLine,
520 window: &mut Window,
521 cx: &mut Context<Self>,
522 ) {
523 let stop_at_indent = action.stop_at_indent && !self.mode.is_single_line();
524 self.change_selections(Default::default(), window, cx, |s| {
525 s.move_heads_with(&mut |map, head, _| {
526 (
527 movement::indented_line_beginning(
528 map,
529 head,
530 action.stop_at_soft_wraps,
531 stop_at_indent,
532 ),
533 SelectionGoal::None,
534 )
535 });
536 });
537 }
538
539 pub fn move_to_end_of_line(
540 &mut self,
541 action: &MoveToEndOfLine,
542 window: &mut Window,
543 cx: &mut Context<Self>,
544 ) {
545 self.change_selections(Default::default(), window, cx, |s| {
546 s.move_cursors_with(&mut |map, head, _| {
547 (
548 movement::line_end(map, head, action.stop_at_soft_wraps),
549 SelectionGoal::None,
550 )
551 });
552 })
553 }
554
555 pub fn select_to_end_of_line(
556 &mut self,
557 action: &SelectToEndOfLine,
558 window: &mut Window,
559 cx: &mut Context<Self>,
560 ) {
561 self.change_selections(Default::default(), window, cx, |s| {
562 s.move_heads_with(&mut |map, head, _| {
563 (
564 movement::line_end(map, head, action.stop_at_soft_wraps),
565 SelectionGoal::None,
566 )
567 });
568 })
569 }
570
571 pub fn move_to_start_of_paragraph(
572 &mut self,
573 _: &MoveToStartOfParagraph,
574 window: &mut Window,
575 cx: &mut Context<Self>,
576 ) {
577 if matches!(self.mode, EditorMode::SingleLine) {
578 cx.propagate();
579 return;
580 }
581 self.change_selections(Default::default(), window, cx, |s| {
582 s.move_with(&mut |map, selection| {
583 selection.collapse_to(
584 movement::start_of_paragraph(map, selection.head(), 1),
585 SelectionGoal::None,
586 )
587 });
588 })
589 }
590
591 pub fn move_to_end_of_paragraph(
592 &mut self,
593 _: &MoveToEndOfParagraph,
594 window: &mut Window,
595 cx: &mut Context<Self>,
596 ) {
597 if matches!(self.mode, EditorMode::SingleLine) {
598 cx.propagate();
599 return;
600 }
601 self.change_selections(Default::default(), window, cx, |s| {
602 s.move_with(&mut |map, selection| {
603 selection.collapse_to(
604 movement::end_of_paragraph(map, selection.head(), 1),
605 SelectionGoal::None,
606 )
607 });
608 })
609 }
610
611 pub fn move_to_next_comment_paragraph(
612 &mut self,
613 _: &MoveToNextCommentParagraph,
614 window: &mut Window,
615 cx: &mut Context<Self>,
616 ) {
617 if matches!(self.mode, EditorMode::SingleLine) {
618 cx.propagate();
619 return;
620 }
621 // Keep the destination paragraph near the top of the viewport so the
622 // whole paragraph below the caret stays visible after a jump.
623 self.change_selections(
624 SelectionEffects::scroll(Autoscroll::top_relative(5.0)),
625 window,
626 cx,
627 |s| {
628 s.move_with(&mut |map, selection| {
629 selection.collapse_to(
630 movement::comment_paragraph(
631 map,
632 selection.head(),
633 workspace::searchable::Direction::Next,
634 ),
635 SelectionGoal::None,
636 )
637 });
638 },
639 )
640 }
641
642 pub fn move_to_previous_comment_paragraph(
643 &mut self,
644 _: &MoveToPreviousCommentParagraph,
645 window: &mut Window,
646 cx: &mut Context<Self>,
647 ) {
648 if matches!(self.mode, EditorMode::SingleLine) {
649 cx.propagate();
650 return;
651 }
652 // Keep the destination paragraph near the top of the viewport so the
653 // whole paragraph below the caret stays visible after a jump.
654 self.change_selections(
655 SelectionEffects::scroll(Autoscroll::top_relative(5.0)),
656 window,
657 cx,
658 |s| {
659 s.move_with(&mut |map, selection| {
660 selection.collapse_to(
661 movement::comment_paragraph(
662 map,
663 selection.head(),
664 workspace::searchable::Direction::Prev,
665 ),
666 SelectionGoal::None,
667 )
668 });
669 },
670 )
671 }
672
673 pub fn select_to_start_of_paragraph(
674 &mut self,
675 _: &SelectToStartOfParagraph,
676 window: &mut Window,
677 cx: &mut Context<Self>,
678 ) {
679 if matches!(self.mode, EditorMode::SingleLine) {
680 cx.propagate();
681 return;
682 }
683 self.change_selections(Default::default(), window, cx, |s| {
684 s.move_heads_with(&mut |map, head, _| {
685 (
686 movement::start_of_paragraph(map, head, 1),
687 SelectionGoal::None,
688 )
689 });
690 })
691 }
692
693 pub fn select_to_end_of_paragraph(
694 &mut self,
695 _: &SelectToEndOfParagraph,
696 window: &mut Window,
697 cx: &mut Context<Self>,
698 ) {
699 if matches!(self.mode, EditorMode::SingleLine) {
700 cx.propagate();
701 return;
702 }
703 self.change_selections(Default::default(), window, cx, |s| {
704 s.move_heads_with(&mut |map, head, _| {
705 (
706 movement::end_of_paragraph(map, head, 1),
707 SelectionGoal::None,
708 )
709 });
710 })
711 }
712
713 pub fn move_to_start_of_excerpt(
714 &mut self,
715 _: &MoveToStartOfExcerpt,
716 window: &mut Window,
717 cx: &mut Context<Self>,
718 ) {
719 if matches!(self.mode, EditorMode::SingleLine) {
720 cx.propagate();
721 return;
722 }
723 self.change_selections(Default::default(), window, cx, |s| {
724 s.move_with(&mut |map, selection| {
725 selection.collapse_to(
726 movement::start_of_excerpt(
727 map,
728 selection.head(),
729 workspace::searchable::Direction::Prev,
730 ),
731 SelectionGoal::None,
732 )
733 });
734 })
735 }
736
737 pub fn move_to_start_of_next_excerpt(
738 &mut self,
739 _: &MoveToStartOfNextExcerpt,
740 window: &mut Window,
741 cx: &mut Context<Self>,
742 ) {
743 if matches!(self.mode, EditorMode::SingleLine) {
744 cx.propagate();
745 return;
746 }
747
748 self.change_selections(Default::default(), window, cx, |s| {
749 s.move_with(&mut |map, selection| {
750 selection.collapse_to(
751 movement::start_of_excerpt(
752 map,
753 selection.head(),
754 workspace::searchable::Direction::Next,
755 ),
756 SelectionGoal::None,
757 )
758 });
759 })
760 }
761
762 pub fn move_to_end_of_excerpt(
763 &mut self,
764 _: &MoveToEndOfExcerpt,
765 window: &mut Window,
766 cx: &mut Context<Self>,
767 ) {
768 if matches!(self.mode, EditorMode::SingleLine) {
769 cx.propagate();
770 return;
771 }
772 self.change_selections(Default::default(), window, cx, |s| {
773 s.move_with(&mut |map, selection| {
774 selection.collapse_to(
775 movement::end_of_excerpt(
776 map,
777 selection.head(),
778 workspace::searchable::Direction::Next,
779 ),
780 SelectionGoal::None,
781 )
782 });
783 })
784 }
785
786 pub fn move_to_end_of_previous_excerpt(
787 &mut self,
788 _: &MoveToEndOfPreviousExcerpt,
789 window: &mut Window,
790 cx: &mut Context<Self>,
791 ) {
792 if matches!(self.mode, EditorMode::SingleLine) {
793 cx.propagate();
794 return;
795 }
796 self.change_selections(Default::default(), window, cx, |s| {
797 s.move_with(&mut |map, selection| {
798 selection.collapse_to(
799 movement::end_of_excerpt(
800 map,
801 selection.head(),
802 workspace::searchable::Direction::Prev,
803 ),
804 SelectionGoal::None,
805 )
806 });
807 })
808 }
809
810 pub fn select_to_start_of_excerpt(
811 &mut self,
812 _: &SelectToStartOfExcerpt,
813 window: &mut Window,
814 cx: &mut Context<Self>,
815 ) {
816 if matches!(self.mode, EditorMode::SingleLine) {
817 cx.propagate();
818 return;
819 }
820 self.change_selections(Default::default(), window, cx, |s| {
821 s.move_heads_with(&mut |map, head, _| {
822 (
823 movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
824 SelectionGoal::None,
825 )
826 });
827 })
828 }
829
830 pub fn select_to_start_of_next_excerpt(
831 &mut self,
832 _: &SelectToStartOfNextExcerpt,
833 window: &mut Window,
834 cx: &mut Context<Self>,
835 ) {
836 if matches!(self.mode, EditorMode::SingleLine) {
837 cx.propagate();
838 return;
839 }
840 self.change_selections(Default::default(), window, cx, |s| {
841 s.move_heads_with(&mut |map, head, _| {
842 (
843 movement::start_of_excerpt(map, head, workspace::searchable::Direction::Next),
844 SelectionGoal::None,
845 )
846 });
847 })
848 }
849
850 pub fn select_to_end_of_excerpt(
851 &mut self,
852 _: &SelectToEndOfExcerpt,
853 window: &mut Window,
854 cx: &mut Context<Self>,
855 ) {
856 if matches!(self.mode, EditorMode::SingleLine) {
857 cx.propagate();
858 return;
859 }
860 self.change_selections(Default::default(), window, cx, |s| {
861 s.move_heads_with(&mut |map, head, _| {
862 (
863 movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
864 SelectionGoal::None,
865 )
866 });
867 })
868 }
869
870 pub fn select_to_end_of_previous_excerpt(
871 &mut self,
872 _: &SelectToEndOfPreviousExcerpt,
873 window: &mut Window,
874 cx: &mut Context<Self>,
875 ) {
876 if matches!(self.mode, EditorMode::SingleLine) {
877 cx.propagate();
878 return;
879 }
880 self.change_selections(Default::default(), window, cx, |s| {
881 s.move_heads_with(&mut |map, head, _| {
882 (
883 movement::end_of_excerpt(map, head, workspace::searchable::Direction::Prev),
884 SelectionGoal::None,
885 )
886 });
887 })
888 }
889
890 pub fn move_to_beginning(
891 &mut self,
892 _: &MoveToBeginning,
893 window: &mut Window,
894 cx: &mut Context<Self>,
895 ) {
896 if matches!(self.mode, EditorMode::SingleLine) {
897 cx.propagate();
898 return;
899 }
900 self.change_selections(Default::default(), window, cx, |s| {
901 s.select_ranges(vec![Anchor::Min..Anchor::Min]);
902 });
903 }
904
905 pub fn select_to_beginning(
906 &mut self,
907 _: &SelectToBeginning,
908 window: &mut Window,
909 cx: &mut Context<Self>,
910 ) {
911 let mut selection = self.selections.last::<Point>(&self.display_snapshot(cx));
912 selection.set_head(Point::zero(), SelectionGoal::None);
913 self.change_selections(Default::default(), window, cx, |s| {
914 s.select(vec![selection]);
915 });
916 }
917
918 pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
919 if matches!(self.mode, EditorMode::SingleLine) {
920 cx.propagate();
921 return;
922 }
923 let cursor = self.buffer.read(cx).read(cx).len();
924 self.change_selections(Default::default(), window, cx, |s| {
925 s.select_ranges(vec![cursor..cursor])
926 });
927 }
928
929 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
930 self.nav_history = nav_history;
931 }
932
933 pub fn save_location(
934 &mut self,
935 _: &SaveLocation,
936 _window: &mut Window,
937 cx: &mut Context<Self>,
938 ) {
939 self.create_nav_history_entry(cx);
940 }
941
942 pub fn create_nav_history_entry(&mut self, cx: &mut Context<Self>) {
943 self.push_to_nav_history(
944 self.selections.newest_anchor().head(),
945 None,
946 false,
947 true,
948 cx,
949 );
950 }
951
952 pub fn expand_excerpts(
953 &mut self,
954 action: &ExpandExcerpts,
955 _: &mut Window,
956 cx: &mut Context<Self>,
957 ) {
958 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
959 }
960
961 pub fn expand_excerpts_down(
962 &mut self,
963 action: &ExpandExcerptsDown,
964 _: &mut Window,
965 cx: &mut Context<Self>,
966 ) {
967 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
968 }
969
970 pub fn expand_excerpts_up(
971 &mut self,
972 action: &ExpandExcerptsUp,
973 _: &mut Window,
974 cx: &mut Context<Self>,
975 ) {
976 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
977 }
978
979 pub fn go_to_singleton_buffer_point(
980 &mut self,
981 point: Point,
982 window: &mut Window,
983 cx: &mut Context<Self>,
984 ) {
985 self.go_to_singleton_buffer_range(point..point, window, cx);
986 }
987
988 pub fn go_to_singleton_buffer_range(
989 &mut self,
990 range: Range<Point>,
991 window: &mut Window,
992 cx: &mut Context<Self>,
993 ) {
994 self.go_to_singleton_buffer_range_impl(range, true, window, cx);
995 }
996
997 /// Like `go_to_singleton_buffer_point`, but does not push a navigation
998 /// history entry. Useful when the caller already recorded one (e.g. when
999 /// a file was just opened and we only need to move the cursor).
1000 pub fn go_to_singleton_buffer_point_silently(
1001 &mut self,
1002 point: Point,
1003 window: &mut Window,
1004 cx: &mut Context<Self>,
1005 ) {
1006 self.go_to_singleton_buffer_range_impl(point..point, false, window, cx);
1007 }
1008
1009 pub fn go_to_next_document_highlight(
1010 &mut self,
1011 _: &GoToNextDocumentHighlight,
1012 window: &mut Window,
1013 cx: &mut Context<Self>,
1014 ) {
1015 self.go_to_document_highlight_before_or_after_position(Direction::Next, window, cx);
1016 }
1017
1018 pub fn go_to_prev_document_highlight(
1019 &mut self,
1020 _: &GoToPreviousDocumentHighlight,
1021 window: &mut Window,
1022 cx: &mut Context<Self>,
1023 ) {
1024 self.go_to_document_highlight_before_or_after_position(Direction::Prev, window, cx);
1025 }
1026
1027 pub fn go_to_definition(
1028 &mut self,
1029 _: &GoToDefinition,
1030 window: &mut Window,
1031 cx: &mut Context<Self>,
1032 ) -> Task<Result<Navigated>> {
1033 let definition =
1034 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
1035 let fallback_strategy = EditorSettings::get_global(cx).go_to_definition_fallback;
1036 cx.spawn_in(window, async move |editor, cx| {
1037 if definition.await? == Navigated::Yes {
1038 return Ok(Navigated::Yes);
1039 }
1040 match fallback_strategy {
1041 GoToDefinitionFallback::None => Ok(Navigated::No),
1042 GoToDefinitionFallback::FindAllReferences => {
1043 match editor.update_in(cx, |editor, window, cx| {
1044 editor.find_all_references(&FindAllReferences::default(), window, cx)
1045 })? {
1046 Some(references) => references.await,
1047 None => Ok(Navigated::No),
1048 }
1049 }
1050 }
1051 })
1052 }
1053
1054 pub fn go_to_declaration(
1055 &mut self,
1056 _: &GoToDeclaration,
1057 window: &mut Window,
1058 cx: &mut Context<Self>,
1059 ) -> Task<Result<Navigated>> {
1060 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
1061 }
1062
1063 pub fn go_to_declaration_split(
1064 &mut self,
1065 _: &GoToDeclaration,
1066 window: &mut Window,
1067 cx: &mut Context<Self>,
1068 ) -> Task<Result<Navigated>> {
1069 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
1070 }
1071
1072 pub fn go_to_implementation(
1073 &mut self,
1074 _: &GoToImplementation,
1075 window: &mut Window,
1076 cx: &mut Context<Self>,
1077 ) -> Task<Result<Navigated>> {
1078 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
1079 }
1080
1081 pub fn go_to_implementation_split(
1082 &mut self,
1083 _: &GoToImplementationSplit,
1084 window: &mut Window,
1085 cx: &mut Context<Self>,
1086 ) -> Task<Result<Navigated>> {
1087 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
1088 }
1089
1090 pub fn go_to_type_definition(
1091 &mut self,
1092 _: &GoToTypeDefinition,
1093 window: &mut Window,
1094 cx: &mut Context<Self>,
1095 ) -> Task<Result<Navigated>> {
1096 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
1097 }
1098
1099 pub fn go_to_definition_split(
1100 &mut self,
1101 _: &GoToDefinitionSplit,
1102 window: &mut Window,
1103 cx: &mut Context<Self>,
1104 ) -> Task<Result<Navigated>> {
1105 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
1106 }
1107
1108 pub fn go_to_type_definition_split(
1109 &mut self,
1110 _: &GoToTypeDefinitionSplit,
1111 window: &mut Window,
1112 cx: &mut Context<Self>,
1113 ) -> Task<Result<Navigated>> {
1114 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
1115 }
1116
1117 pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
1118 let selection = self.selections.newest_anchor();
1119 let head = selection.head();
1120 let tail = selection.tail();
1121
1122 let Some((buffer, start_position)) =
1123 self.buffer.read(cx).text_anchor_for_position(head, cx)
1124 else {
1125 return;
1126 };
1127
1128 let end_position = if head != tail {
1129 let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
1130 return;
1131 };
1132 Some(pos)
1133 } else {
1134 None
1135 };
1136
1137 let url_finder = cx.spawn_in(window, async move |_editor, cx| {
1138 let url = if let Some(end_pos) = end_position {
1139 find_url_from_range(&buffer, start_position..end_pos, cx)
1140 } else {
1141 find_url(&buffer, start_position, cx).map(|(_, url)| url)
1142 };
1143
1144 if let Some(url) = url {
1145 cx.update(|window, cx| {
1146 if parse_zed_link(&url, cx).is_some() {
1147 window.dispatch_action(
1148 Box::new(zed_actions::OpenAppUrl { url: url.into() }),
1149 cx,
1150 );
1151 } else {
1152 cx.open_url(&url);
1153 }
1154 })?;
1155 }
1156
1157 anyhow::Ok(())
1158 });
1159
1160 url_finder.detach();
1161 }
1162
1163 pub fn open_selected_filename(
1164 &mut self,
1165 _: &OpenSelectedFilename,
1166 window: &mut Window,
1167 cx: &mut Context<Self>,
1168 ) {
1169 let Some(workspace) = self.workspace() else {
1170 return;
1171 };
1172
1173 let position = self.selections.newest_anchor().head();
1174
1175 let Some((buffer, buffer_position)) =
1176 self.buffer.read(cx).text_anchor_for_position(position, cx)
1177 else {
1178 return;
1179 };
1180
1181 let project = self.project.clone();
1182
1183 cx.spawn_in(window, async move |_, cx| {
1184 let result = find_file(&buffer, project, buffer_position, cx).await;
1185
1186 if let Some((_, file_target)) = result {
1187 let item = workspace
1188 .update_in(cx, |workspace, window, cx| {
1189 workspace.open_resolved_path(file_target.resolved_path.clone(), window, cx)
1190 })?
1191 .await?;
1192
1193 file_target.navigate_item_to_position(item, cx);
1194 }
1195 anyhow::Ok(())
1196 })
1197 .detach();
1198 }
1199
1200 pub fn go_to_reference_before_or_after_position(
1201 &mut self,
1202 direction: Direction,
1203 count: usize,
1204 window: &mut Window,
1205 cx: &mut Context<Self>,
1206 ) -> Option<Task<Result<()>>> {
1207 let selection = self.selections.newest_anchor();
1208 let head = selection.head();
1209
1210 let multi_buffer = self.buffer.read(cx);
1211
1212 let (buffer, text_head) = multi_buffer.text_anchor_for_position(head, cx)?;
1213 let workspace = self.workspace()?;
1214 let project = workspace.read(cx).project().clone();
1215 let references =
1216 project.update(cx, |project, cx| project.references(&buffer, text_head, cx));
1217 Some(cx.spawn_in(window, async move |editor, cx| -> Result<()> {
1218 let Some(locations) = references.await? else {
1219 return Ok(());
1220 };
1221
1222 if locations.is_empty() {
1223 // totally normal - the cursor may be on something which is not
1224 // a symbol (e.g. a keyword)
1225 log::info!("no references found under cursor");
1226 return Ok(());
1227 }
1228
1229 let multi_buffer = editor.read_with(cx, |editor, _| editor.buffer().clone())?;
1230
1231 let (locations, current_location_index) =
1232 multi_buffer.update(cx, |multi_buffer, cx| {
1233 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
1234 let mut locations = locations
1235 .into_iter()
1236 .filter_map(|loc| {
1237 let start = multi_buffer_snapshot.anchor_in_excerpt(loc.range.start)?;
1238 let end = multi_buffer_snapshot.anchor_in_excerpt(loc.range.end)?;
1239 Some(start..end)
1240 })
1241 .collect::<Vec<_>>();
1242 // There is an O(n) implementation, but given this list will be
1243 // small (usually <100 items), the extra O(log(n)) factor isn't
1244 // worth the (surprisingly large amount of) extra complexity.
1245 locations
1246 .sort_unstable_by(|l, r| l.start.cmp(&r.start, &multi_buffer_snapshot));
1247
1248 let head_offset = head.to_offset(&multi_buffer_snapshot);
1249
1250 let current_location_index = locations.iter().position(|loc| {
1251 loc.start.to_offset(&multi_buffer_snapshot) <= head_offset
1252 && loc.end.to_offset(&multi_buffer_snapshot) >= head_offset
1253 });
1254
1255 (locations, current_location_index)
1256 });
1257
1258 let Some(current_location_index) = current_location_index else {
1259 // This indicates something has gone wrong, because we already
1260 // handle the "no references" case above
1261 log::error!(
1262 "failed to find current reference under cursor. Total references: {}",
1263 locations.len()
1264 );
1265 return Ok(());
1266 };
1267
1268 let destination_location_index = match direction {
1269 Direction::Next => (current_location_index + count) % locations.len(),
1270 Direction::Prev => {
1271 (current_location_index + locations.len() - count % locations.len())
1272 % locations.len()
1273 }
1274 };
1275
1276 // TODO(cameron): is this needed?
1277 // the thinking is to avoid "jumping to the current location" (avoid
1278 // polluting "jumplist" in vim terms)
1279 if current_location_index == destination_location_index {
1280 return Ok(());
1281 }
1282
1283 let Range { start, end } = locations[destination_location_index];
1284
1285 editor.update_in(cx, |editor, window, cx| {
1286 let effects = SelectionEffects::scroll(Autoscroll::for_go_to_definition(
1287 editor.cursor_top_offset(cx),
1288 cx,
1289 ));
1290
1291 editor.unfold_ranges(&[start..end], false, false, cx);
1292 editor.change_selections(effects, window, cx, |s| {
1293 s.select_ranges([start..start]);
1294 });
1295 })?;
1296
1297 Ok(())
1298 }))
1299 }
1300
1301 /// Runs the LSP go-to query for `kind` (definition / declaration / type /
1302 /// implementation) against the symbol under the cursor and returns the raw
1303 /// target [`Location`]s. The go-to counterpart to
1304 /// [`Self::find_all_references_locations`]; used by the LSP location pickers.
1305 pub fn definition_locations_of_kind(
1306 &mut self,
1307 kind: GotoDefinitionKind,
1308 cx: &mut Context<Self>,
1309 ) -> Option<Task<Result<Vec<Location>>>> {
1310 let provider = self.semantics_provider.clone()?;
1311 let selection = self.selections.newest_anchor();
1312 let multi_buffer = self.buffer.read(cx);
1313 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
1314 let head = selection
1315 .map(|anchor| anchor.to_offset(&multi_buffer_snapshot))
1316 .head();
1317
1318 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
1319 let definitions = provider.definitions(&buffer, head, kind, cx)?;
1320 Some(cx.spawn(async move |editor, cx| {
1321 let definitions = definitions.await?.unwrap_or_default();
1322 // Drop a result that points back at the cursor, matching
1323 // `go_to_definition_of_kind` (otherwise the picker lists the symbol
1324 // you invoked it on).
1325 editor.update(cx, |_, cx| {
1326 definitions
1327 .into_iter()
1328 .filter(|link| hover_links::exclude_link_to_position(&buffer, &head, link, cx))
1329 .map(|link| link.target)
1330 .collect()
1331 })
1332 }))
1333 }
1334
1335 /// Runs an LSP "find all references" query for the symbol under the cursor
1336 /// and returns the raw [`Location`]s. Unlike [`Self::find_all_references`],
1337 /// this does not group the results or open any UI
1338 pub fn find_all_references_locations(
1339 &mut self,
1340 project: &Entity<Project>,
1341 cx: &mut Context<Self>,
1342 ) -> Option<Task<Result<Vec<Location>>>> {
1343 let selection = self.selections.newest_anchor();
1344 let multi_buffer = self.buffer.read(cx);
1345 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
1346 let head = selection
1347 .map(|anchor| anchor.to_offset(&multi_buffer_snapshot))
1348 .head();
1349
1350 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
1351 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
1352 // Keep every reference, including the one under the cursor, to match the
1353 // default `find_all_references` multibuffer (`always_open_multibuffer`).
1354 Some(cx.spawn(async move |_, _| Ok(references.await?.unwrap_or_default())))
1355 }
1356
1357 pub fn find_all_references(
1358 &mut self,
1359 action: &FindAllReferences,
1360 window: &mut Window,
1361 cx: &mut Context<Self>,
1362 ) -> Option<Task<Result<Navigated>>> {
1363 let always_open_multibuffer = action.always_open_multibuffer;
1364 let selection = self.selections.newest_anchor();
1365 let multi_buffer = self.buffer.read(cx);
1366 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
1367 let selection_offset = selection.map(|anchor| anchor.to_offset(&multi_buffer_snapshot));
1368 let selection_point = selection.map(|anchor| anchor.to_point(&multi_buffer_snapshot));
1369 let head = selection_offset.head();
1370
1371 let head_anchor = multi_buffer_snapshot.anchor_at(
1372 head,
1373 if head < selection_offset.tail() {
1374 Bias::Right
1375 } else {
1376 Bias::Left
1377 },
1378 );
1379
1380 match self
1381 .find_all_references_task_sources
1382 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
1383 {
1384 Ok(_) => {
1385 log::info!(
1386 "Ignoring repeated FindAllReferences invocation with the position of already running task"
1387 );
1388 return None;
1389 }
1390 Err(i) => {
1391 self.find_all_references_task_sources.insert(i, head_anchor);
1392 }
1393 }
1394
1395 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
1396 let workspace = self.workspace()?;
1397 let project = workspace.read(cx).project().clone();
1398 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
1399 Some(cx.spawn_in(window, async move |editor, cx| {
1400 let _cleanup = cx.on_drop(&editor, move |editor, _| {
1401 if let Ok(i) = editor
1402 .find_all_references_task_sources
1403 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
1404 {
1405 editor.find_all_references_task_sources.remove(i);
1406 }
1407 });
1408
1409 let Some(locations) = references.await? else {
1410 return anyhow::Ok(Navigated::No);
1411 };
1412 let mut locations = cx.update(|_, cx| {
1413 locations
1414 .into_iter()
1415 .map(|location| {
1416 let buffer = location.buffer.read(cx);
1417 (location.buffer, location.range.to_point(buffer))
1418 })
1419 // if special-casing the single-match case, remove ranges
1420 // that intersect current selection
1421 .filter(|(location_buffer, location)| {
1422 if always_open_multibuffer || &buffer != location_buffer {
1423 return true;
1424 }
1425
1426 !location.contains_inclusive(&selection_point.range())
1427 })
1428 .into_group_map()
1429 })?;
1430 if locations.is_empty() {
1431 return anyhow::Ok(Navigated::No);
1432 }
1433 for ranges in locations.values_mut() {
1434 ranges.sort_unstable_by_key(|range| (range.start, Reverse(range.end)));
1435 ranges.dedup();
1436 }
1437 let mut num_locations = 0;
1438 for ranges in locations.values_mut() {
1439 ranges.sort_unstable_by_key(|range| (range.start, Reverse(range.end)));
1440 ranges.dedup();
1441 num_locations += ranges.len();
1442 }
1443
1444 if num_locations == 1 && !always_open_multibuffer {
1445 let Some((target_buffer, target_ranges)) = locations.into_iter().next() else {
1446 return anyhow::Ok(Navigated::No);
1447 };
1448 let Some(target_range) = target_ranges.first().cloned() else {
1449 return anyhow::Ok(Navigated::No);
1450 };
1451
1452 return editor.update_in(cx, |editor, window, cx| {
1453 let range = target_range.to_point(target_buffer.read(cx));
1454 let range = editor.range_for_match(&range);
1455 let range = range.start..range.start;
1456
1457 if Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
1458 editor.go_to_singleton_buffer_range(range, window, cx);
1459 } else {
1460 let pane = workspace.read(cx).active_pane().clone();
1461 window.defer(cx, move |window, cx| {
1462 let target_editor: Entity<Self> =
1463 workspace.update(cx, |workspace, cx| {
1464 let pane = workspace.active_pane().clone();
1465
1466 let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
1467 let keep_old_preview = preview_tabs_settings
1468 .enable_keep_preview_on_code_navigation;
1469 let allow_new_preview = preview_tabs_settings
1470 .enable_preview_file_from_code_navigation;
1471
1472 workspace.open_project_item(
1473 pane,
1474 target_buffer.clone(),
1475 true,
1476 true,
1477 keep_old_preview,
1478 allow_new_preview,
1479 window,
1480 cx,
1481 )
1482 });
1483 target_editor.update(cx, |target_editor, cx| {
1484 // When selecting a definition in a different buffer, disable the nav history
1485 // to avoid creating a history entry at the previous cursor location.
1486 pane.update(cx, |pane, _| pane.disable_history());
1487 target_editor.go_to_singleton_buffer_range(range, window, cx);
1488 pane.update(cx, |pane, _| pane.enable_history());
1489 });
1490 });
1491 }
1492 Navigated::No
1493 });
1494 }
1495
1496 workspace.update_in(cx, |workspace, window, cx| {
1497 let target = locations
1498 .iter()
1499 .flat_map(|(k, v)| iter::repeat(k.clone()).zip(v))
1500 .map(|(buffer, location)| {
1501 buffer
1502 .read(cx)
1503 .text_for_range(location.clone())
1504 .collect::<String>()
1505 })
1506 .filter(|text| !text.contains('\n'))
1507 .unique()
1508 .take(3)
1509 .join(", ");
1510 let title = if target.is_empty() {
1511 "References".to_owned()
1512 } else {
1513 format!("References to {target}")
1514 };
1515 let allow_preview = PreviewTabsSettings::get_global(cx)
1516 .enable_preview_multibuffer_from_code_navigation;
1517 Self::open_locations_in_multibuffer(
1518 workspace,
1519 locations,
1520 title,
1521 false,
1522 allow_preview,
1523 MultibufferSelectionMode::First,
1524 window,
1525 cx,
1526 );
1527 Navigated::Yes
1528 })
1529 }))
1530 }
1531
1532 pub(super) fn navigation_entry(
1533 &self,
1534 cursor_anchor: Anchor,
1535 cx: &mut Context<Self>,
1536 ) -> Option<NavigationEntry> {
1537 let Some(history) = self.nav_history.clone() else {
1538 return None;
1539 };
1540 let data = self.navigation_data(cursor_anchor, cx);
1541 Some(history.navigation_entry(Some(Arc::new(data) as Arc<dyn Any + Send + Sync>)))
1542 }
1543
1544 pub(super) fn push_to_nav_history(
1545 &mut self,
1546 cursor_anchor: Anchor,
1547 new_position: Option<Point>,
1548 is_deactivate: bool,
1549 always: bool,
1550 cx: &mut Context<Self>,
1551 ) {
1552 let data = self.navigation_data(cursor_anchor, cx);
1553 if let Some(nav_history) = self.nav_history.as_mut() {
1554 if let Some(new_position) = new_position {
1555 let row_delta = (new_position.row as i64 - data.cursor_position.row as i64).abs();
1556 if row_delta == 0 || (row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA && !always) {
1557 return;
1558 }
1559 }
1560
1561 let cursor_row = data.cursor_position.row;
1562 nav_history.push(Some(data), Some(cursor_row), cx);
1563 cx.emit(EditorEvent::PushedToNavHistory {
1564 anchor: cursor_anchor,
1565 is_deactivate,
1566 })
1567 }
1568 }
1569
1570 pub(super) fn expand_excerpt(
1571 &mut self,
1572 excerpt_anchor: Anchor,
1573 direction: ExpandExcerptDirection,
1574 window: &mut Window,
1575 cx: &mut Context<Self>,
1576 ) {
1577 let lines_to_expand = EditorSettings::get_global(cx).expand_excerpt_lines;
1578
1579 if self.delegate_expand_excerpts {
1580 cx.emit(EditorEvent::ExpandExcerptsRequested {
1581 excerpt_anchors: vec![excerpt_anchor],
1582 lines: lines_to_expand,
1583 direction,
1584 });
1585 return;
1586 }
1587
1588 let current_scroll_position = self.scroll_position(cx);
1589 let mut scroll = None;
1590
1591 if direction == ExpandExcerptDirection::Down {
1592 let multi_buffer = self.buffer.read(cx);
1593 let snapshot = multi_buffer.snapshot(cx);
1594 if let Some((buffer_snapshot, excerpt_range)) =
1595 snapshot.excerpt_containing(excerpt_anchor..excerpt_anchor)
1596 {
1597 let excerpt_end_row =
1598 Point::from_anchor(&excerpt_range.context.end, &buffer_snapshot).row;
1599 let last_row = buffer_snapshot.max_point().row;
1600 let lines_below = last_row.saturating_sub(excerpt_end_row);
1601 if lines_below >= lines_to_expand {
1602 scroll = Some(
1603 current_scroll_position
1604 + gpui::Point::new(0.0, lines_to_expand as ScrollOffset),
1605 );
1606 }
1607 }
1608 }
1609 if direction == ExpandExcerptDirection::Up
1610 && self
1611 .buffer
1612 .read(cx)
1613 .snapshot(cx)
1614 .excerpt_before(excerpt_anchor)
1615 .is_none()
1616 {
1617 scroll = Some(current_scroll_position);
1618 }
1619
1620 self.buffer.update(cx, |buffer, cx| {
1621 buffer.expand_excerpts([excerpt_anchor], lines_to_expand, direction, cx)
1622 });
1623
1624 if let Some(new_scroll_position) = scroll {
1625 self.set_scroll_position(new_scroll_position, window, cx);
1626 }
1627 }
1628
1629 pub(super) fn go_to_next_change(
1630 &mut self,
1631 _: &GoToNextChange,
1632 window: &mut Window,
1633 cx: &mut Context<Self>,
1634 ) {
1635 if let Some(selections) = self
1636 .change_list
1637 .next_change(1, Direction::Next)
1638 .map(|s| s.to_vec())
1639 {
1640 self.change_selections(Default::default(), window, cx, |s| {
1641 let map = s.display_snapshot();
1642 s.select_display_ranges(selections.iter().map(|a| {
1643 let point = a.to_display_point(&map);
1644 point..point
1645 }))
1646 })
1647 }
1648 }
1649
1650 pub(super) fn go_to_previous_change(
1651 &mut self,
1652 _: &GoToPreviousChange,
1653 window: &mut Window,
1654 cx: &mut Context<Self>,
1655 ) {
1656 if let Some(selections) = self
1657 .change_list
1658 .next_change(1, Direction::Prev)
1659 .map(|s| s.to_vec())
1660 {
1661 self.change_selections(Default::default(), window, cx, |s| {
1662 let map = s.display_snapshot();
1663 s.select_display_ranges(selections.iter().map(|a| {
1664 let point = a.to_display_point(&map);
1665 point..point
1666 }))
1667 })
1668 }
1669 }
1670
1671 pub(super) fn go_to_line<T: 'static>(
1672 &mut self,
1673 position: Anchor,
1674 highlight_color: fn(&App) -> Hsla,
1675 window: &mut Window,
1676 cx: &mut Context<Self>,
1677 ) {
1678 let snapshot = self.snapshot(window, cx).display_snapshot;
1679 let position = position.to_point(&snapshot.buffer_snapshot());
1680 let start = snapshot
1681 .buffer_snapshot()
1682 .clip_point(Point::new(position.row, 0), Bias::Left);
1683 let end = start + Point::new(1, 0);
1684 let start = snapshot.buffer_snapshot().anchor_before(start);
1685 let end = snapshot.buffer_snapshot().anchor_before(end);
1686
1687 self.highlight_rows::<T>(start..end, highlight_color, Default::default(), cx);
1688
1689 if self.buffer.read(cx).is_singleton() {
1690 self.request_autoscroll(Autoscroll::center().for_anchor(start), cx);
1691 }
1692 }
1693
1694 pub fn navigate_to_hover_links(
1695 &mut self,
1696 kind: Option<GotoDefinitionKind>,
1697 definitions: Vec<HoverLink>,
1698 origin: Option<NavigationEntry>,
1699 split: bool,
1700 window: &mut Window,
1701 cx: &mut Context<Editor>,
1702 ) -> Task<Result<Navigated>> {
1703 // Separate out url and file links, we can only handle one of them at most or an arbitrary number of locations
1704 let mut first_url_or_file = None;
1705 let definitions: Vec<_> = definitions
1706 .into_iter()
1707 .filter_map(|def| match def {
1708 HoverLink::Text(link) => Some(Task::ready(anyhow::Ok(Some(link.target)))),
1709 HoverLink::LspLocation(lsp_location, server_id) => {
1710 let computation =
1711 self.compute_target_location(lsp_location, server_id, window, cx);
1712 Some(cx.background_spawn(computation))
1713 }
1714 HoverLink::Url(url) => {
1715 first_url_or_file = Some(Either::Left(url));
1716 None
1717 }
1718 HoverLink::File(file_target) => {
1719 first_url_or_file = Some(Either::Right(file_target));
1720 None
1721 }
1722 })
1723 .collect();
1724
1725 let workspace = self.workspace();
1726
1727 let excerpt_context_lines = multi_buffer::excerpt_context_lines(cx);
1728 cx.spawn_in(window, async move |editor, cx| {
1729 let locations: Vec<Location> = future::join_all(definitions)
1730 .await
1731 .into_iter()
1732 .filter_map(|location| location.transpose())
1733 .collect::<Result<_>>()
1734 .context("location tasks")?;
1735 let mut locations = cx.update(|_, cx| {
1736 locations
1737 .into_iter()
1738 .map(|location| {
1739 let buffer = location.buffer.read(cx);
1740 (location.buffer, location.range.to_point(buffer))
1741 })
1742 .into_group_map()
1743 })?;
1744 let mut num_locations = 0;
1745 for ranges in locations.values_mut() {
1746 ranges.sort_unstable_by_key(|range| (range.start, Reverse(range.end)));
1747 ranges.dedup();
1748 // Merge overlapping or contained ranges. After sorting by
1749 // (start, Reverse(end)), we can merge in a single pass:
1750 // if the next range starts before the current one ends,
1751 // extend the current range's end if needed.
1752 let mut i = 0;
1753 while i + 1 < ranges.len() {
1754 if ranges[i + 1].start <= ranges[i].end {
1755 let merged_end = ranges[i].end.max(ranges[i + 1].end);
1756 ranges[i].end = merged_end;
1757 ranges.remove(i + 1);
1758 } else {
1759 i += 1;
1760 }
1761 }
1762 let fits_in_one_excerpt = ranges
1763 .iter()
1764 .tuple_windows()
1765 .all(|(a, b)| b.start.row - a.end.row <= 2 * excerpt_context_lines);
1766 num_locations += if fits_in_one_excerpt { 1 } else { ranges.len() };
1767 }
1768
1769 if num_locations > 1 {
1770 let tab_kind = match kind {
1771 Some(GotoDefinitionKind::Implementation) => "Implementations",
1772 Some(GotoDefinitionKind::Symbol) | None => "Definitions",
1773 Some(GotoDefinitionKind::Declaration) => "Declarations",
1774 Some(GotoDefinitionKind::Type) => "Types",
1775 };
1776 let title = editor
1777 .update_in(cx, |_, _, cx| {
1778 let target = locations
1779 .iter()
1780 .flat_map(|(k, v)| iter::repeat(k.clone()).zip(v))
1781 .map(|(buffer, location)| {
1782 buffer
1783 .read(cx)
1784 .text_for_range(location.clone())
1785 .collect::<String>()
1786 })
1787 .filter(|text| !text.contains('\n'))
1788 .unique()
1789 .take(3)
1790 .join(", ");
1791 if target.is_empty() {
1792 tab_kind.to_owned()
1793 } else {
1794 format!("{tab_kind} for {target}")
1795 }
1796 })
1797 .context("buffer title")?;
1798
1799 let Some(workspace) = workspace else {
1800 return Ok(Navigated::No);
1801 };
1802
1803 let opened = workspace
1804 .update_in(cx, |workspace, window, cx| {
1805 let allow_preview = PreviewTabsSettings::get_global(cx)
1806 .enable_preview_multibuffer_from_code_navigation;
1807 if let Some((target_editor, target_pane)) =
1808 Self::open_locations_in_multibuffer(
1809 workspace,
1810 locations,
1811 title,
1812 split,
1813 allow_preview,
1814 MultibufferSelectionMode::First,
1815 window,
1816 cx,
1817 )
1818 {
1819 // We create our own nav history instead of using
1820 // `target_editor.nav_history` because `nav_history`
1821 // seems to be populated asynchronously when an item
1822 // is added to a pane
1823 let mut nav_history = target_pane
1824 .update(cx, |pane, _| pane.nav_history_for_item(&target_editor));
1825 target_editor.update(cx, |editor, cx| {
1826 let nav_data = editor
1827 .navigation_data(editor.selections.newest_anchor().head(), cx);
1828 let target =
1829 Some(nav_history.navigation_entry(Some(
1830 Arc::new(nav_data) as Arc<dyn Any + Send + Sync>
1831 )));
1832 nav_history.push_tag(origin, target);
1833 })
1834 }
1835 })
1836 .is_ok();
1837
1838 anyhow::Ok(Navigated::from_bool(opened))
1839 } else if num_locations == 0 {
1840 // If there is one url or file, open it directly
1841 match first_url_or_file {
1842 Some(Either::Left(url)) => {
1843 cx.update(|window, cx| {
1844 if parse_zed_link(&url, cx).is_some() {
1845 window.dispatch_action(
1846 Box::new(zed_actions::OpenAppUrl { url: url.into() }),
1847 cx,
1848 );
1849 } else {
1850 cx.open_url(&url);
1851 }
1852 })?;
1853 Ok(Navigated::Yes)
1854 }
1855 Some(Either::Right(file_target)) => {
1856 // TODO(andrew): respect preview tab settings
1857 // `enable_keep_preview_on_code_navigation` and
1858 // `enable_preview_file_from_code_navigation`
1859 let Some(workspace) = workspace else {
1860 return Ok(Navigated::No);
1861 };
1862 let item = workspace
1863 .update_in(cx, |workspace, window, cx| {
1864 workspace.open_resolved_path(
1865 file_target.resolved_path.clone(),
1866 window,
1867 cx,
1868 )
1869 })?
1870 .await?;
1871
1872 file_target.navigate_item_to_position(item, cx);
1873
1874 Ok(Navigated::Yes)
1875 }
1876 None => Ok(Navigated::No),
1877 }
1878 } else {
1879 let Some((target_buffer, target_ranges)) = locations.into_iter().next() else {
1880 return Ok(Navigated::No);
1881 };
1882
1883 editor.update_in(cx, |editor, window, cx| {
1884 let target_ranges = target_ranges
1885 .into_iter()
1886 .map(|r| editor.range_for_match(&r))
1887 .map(collapse_multiline_range)
1888 .collect::<Vec<_>>();
1889 if !split
1890 && Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref()
1891 {
1892 let multibuffer = editor.buffer.read(cx);
1893 let target_ranges = target_ranges
1894 .into_iter()
1895 .filter_map(|r| {
1896 let start = multibuffer.buffer_point_to_anchor(
1897 &target_buffer,
1898 r.start,
1899 cx,
1900 )?;
1901 let end = multibuffer.buffer_point_to_anchor(
1902 &target_buffer,
1903 r.end,
1904 cx,
1905 )?;
1906 Some(start..end)
1907 })
1908 .collect::<Vec<_>>();
1909 if target_ranges.is_empty() {
1910 return Navigated::No;
1911 }
1912
1913 editor.change_selections(
1914 SelectionEffects::scroll(Autoscroll::for_go_to_definition(
1915 editor.cursor_top_offset(cx),
1916 cx,
1917 ))
1918 .nav_history(true),
1919 window,
1920 cx,
1921 |s| s.select_anchor_ranges(target_ranges),
1922 );
1923
1924 let target =
1925 editor.navigation_entry(editor.selections.newest_anchor().head(), cx);
1926 if let Some(mut nav_history) = editor.nav_history.clone() {
1927 nav_history.push_tag(origin, target);
1928 }
1929 } else {
1930 let Some(workspace) = workspace else {
1931 return Navigated::No;
1932 };
1933 let pane = workspace.read(cx).active_pane().clone();
1934 let offset = editor.cursor_top_offset(cx);
1935
1936 window.defer(cx, move |window, cx| {
1937 let (target_editor, target_pane): (Entity<Self>, Entity<Pane>) =
1938 workspace.update(cx, |workspace, cx| {
1939 let pane = if split {
1940 workspace.adjacent_pane(window, cx)
1941 } else {
1942 workspace.active_pane().clone()
1943 };
1944
1945 let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
1946 let keep_old_preview = preview_tabs_settings
1947 .enable_keep_preview_on_code_navigation;
1948 let allow_new_preview = preview_tabs_settings
1949 .enable_preview_file_from_code_navigation;
1950
1951 let editor = workspace.open_project_item(
1952 pane.clone(),
1953 target_buffer.clone(),
1954 true,
1955 true,
1956 keep_old_preview,
1957 allow_new_preview,
1958 window,
1959 cx,
1960 );
1961 (editor, pane)
1962 });
1963 // We create our own nav history instead of using
1964 // `target_editor.nav_history` because `nav_history`
1965 // seems to be populated asynchronously when an item
1966 // is added to a pane
1967 let mut nav_history = target_pane
1968 .update(cx, |pane, _| pane.nav_history_for_item(&target_editor));
1969 target_editor.update(cx, |target_editor, cx| {
1970 // When selecting a definition in a different buffer, disable the nav history
1971 // to avoid creating a history entry at the previous cursor location.
1972 pane.update(cx, |pane, _| pane.disable_history());
1973
1974 let multibuffer = target_editor.buffer.read(cx);
1975 let Some(target_buffer) = multibuffer.as_singleton() else {
1976 return Navigated::No;
1977 };
1978 let target_ranges = target_ranges
1979 .into_iter()
1980 .filter_map(|r| {
1981 let start = multibuffer.buffer_point_to_anchor(
1982 &target_buffer,
1983 r.start,
1984 cx,
1985 )?;
1986 let end = multibuffer.buffer_point_to_anchor(
1987 &target_buffer,
1988 r.end,
1989 cx,
1990 )?;
1991 Some(start..end)
1992 })
1993 .collect::<Vec<_>>();
1994 if target_ranges.is_empty() {
1995 return Navigated::No;
1996 }
1997
1998 target_editor.change_selections(
1999 SelectionEffects::scroll(Autoscroll::for_go_to_definition(
2000 offset, cx,
2001 ))
2002 .nav_history(true),
2003 window,
2004 cx,
2005 |s| s.select_anchor_ranges(target_ranges),
2006 );
2007
2008 let nav_data = target_editor.navigation_data(
2009 target_editor.selections.newest_anchor().head(),
2010 cx,
2011 );
2012 let target =
2013 Some(nav_history.navigation_entry(Some(
2014 Arc::new(nav_data) as Arc<dyn Any + Send + Sync>
2015 )));
2016 nav_history.push_tag(origin, target);
2017 pane.update(cx, |pane, _| pane.enable_history());
2018 Navigated::Yes
2019 });
2020 });
2021 }
2022 Navigated::Yes
2023 })
2024 }
2025 })
2026 }
2027
2028 pub(super) fn go_to_next_reference(
2029 &mut self,
2030 _: &GoToNextReference,
2031 window: &mut Window,
2032 cx: &mut Context<Self>,
2033 ) {
2034 let task = self.go_to_reference_before_or_after_position(Direction::Next, 1, window, cx);
2035 if let Some(task) = task {
2036 task.detach();
2037 };
2038 }
2039
2040 pub(super) fn go_to_prev_reference(
2041 &mut self,
2042 _: &GoToPreviousReference,
2043 window: &mut Window,
2044 cx: &mut Context<Self>,
2045 ) {
2046 let task = self.go_to_reference_before_or_after_position(Direction::Prev, 1, window, cx);
2047 if let Some(task) = task {
2048 task.detach();
2049 };
2050 }
2051
2052 pub(super) fn go_to_symbol_by_offset(
2053 &mut self,
2054 window: &mut Window,
2055 cx: &mut Context<Self>,
2056 offset: i8,
2057 ) -> Task<Result<()>> {
2058 let editor_snapshot = self.snapshot(window, cx);
2059
2060 // We don't care about multi-buffer symbols
2061 if !editor_snapshot.is_singleton() {
2062 return Task::ready(Ok(()));
2063 }
2064
2065 let cursor_offset = self
2066 .selections
2067 .newest::<MultiBufferOffset>(&editor_snapshot.display_snapshot)
2068 .head();
2069
2070 cx.spawn_in(window, async move |editor, wcx| -> Result<()> {
2071 let Ok(Some(remote_id)) = editor.update(wcx, |ed, cx| {
2072 let buffer = ed.buffer.read(cx).as_singleton()?;
2073 Some(buffer.read(cx).remote_id())
2074 }) else {
2075 return Ok(());
2076 };
2077
2078 let task = editor.update(wcx, |ed, cx| ed.buffer_outline_items(remote_id, cx))?;
2079 let outline_items: Vec<OutlineItem<text::Anchor>> = task.await;
2080
2081 let multi_snapshot = editor_snapshot.buffer();
2082 let buffer_range = |range: &Range<_>| {
2083 Some(
2084 multi_snapshot
2085 .buffer_anchor_range_to_anchor_range(range.clone())?
2086 .to_offset(multi_snapshot),
2087 )
2088 };
2089
2090 wcx.update_window(wcx.window_handle(), |_, window, acx| {
2091 let current_idx = outline_items
2092 .iter()
2093 .enumerate()
2094 .filter_map(|(idx, item)| {
2095 // Find the closest outline item by distance between outline text and cursor location
2096 let source_range = buffer_range(&item.source_range_for_text)?;
2097 let distance_to_closest_endpoint = cmp::min(
2098 (source_range.start.0 as isize - cursor_offset.0 as isize).abs(),
2099 (source_range.end.0 as isize - cursor_offset.0 as isize).abs(),
2100 );
2101
2102 let item_towards_offset =
2103 (source_range.start.0 as isize - cursor_offset.0 as isize).signum()
2104 == (offset as isize).signum();
2105
2106 let source_range_contains_cursor = source_range.contains(&cursor_offset);
2107
2108 // To pick the next outline to jump to, we should jump in the direction of the offset, and
2109 // we should not already be within the outline's source range. We then pick the closest outline
2110 // item.
2111 (item_towards_offset && !source_range_contains_cursor)
2112 .then_some((distance_to_closest_endpoint, idx))
2113 })
2114 .min()
2115 .map(|(_, idx)| idx);
2116
2117 let Some(idx) = current_idx else {
2118 return;
2119 };
2120
2121 let Some(range) = buffer_range(&outline_items[idx].source_range_for_text) else {
2122 return;
2123 };
2124 let selection = [range.start..range.start];
2125
2126 editor
2127 .update(acx, |editor, ecx| {
2128 editor.change_selections(
2129 SelectionEffects::scroll(Autoscroll::newest()),
2130 window,
2131 ecx,
2132 |s| s.select_ranges(selection),
2133 );
2134 })
2135 .log_err();
2136 })?;
2137
2138 Ok(())
2139 })
2140 }
2141
2142 pub(super) fn go_to_next_symbol(
2143 &mut self,
2144 _: &GoToNextSymbol,
2145 window: &mut Window,
2146 cx: &mut Context<Self>,
2147 ) {
2148 self.go_to_symbol_by_offset(window, cx, 1).detach();
2149 }
2150
2151 pub(super) fn go_to_previous_symbol(
2152 &mut self,
2153 _: &GoToPreviousSymbol,
2154 window: &mut Window,
2155 cx: &mut Context<Self>,
2156 ) {
2157 self.go_to_symbol_by_offset(window, cx, -1).detach();
2158 }
2159
2160 /// Opens `location` and jumps to it through the same path as
2161 /// go-to-definition, so selection, autoscroll, and jumplist tagging all
2162 /// match. `split` opens it in the adjacent pane. Called on the editor the
2163 /// jump originates from.
2164 pub fn open_location(
2165 &mut self,
2166 location: Location,
2167 split: bool,
2168 window: &mut Window,
2169 cx: &mut Context<Self>,
2170 ) -> Task<Result<Navigated>> {
2171 let origin = self.navigation_entry(self.selections.newest_anchor().head(), cx);
2172 let link = HoverLink::Text(LocationLink {
2173 origin: None,
2174 target: location,
2175 });
2176 self.navigate_to_hover_links(None, vec![link], origin, split, window, cx)
2177 }
2178
2179 /// Opens a multibuffer with the given project locations in it.
2180 pub(super) fn open_locations_in_multibuffer(
2181 workspace: &mut Workspace,
2182 locations: std::collections::HashMap<Entity<Buffer>, Vec<Range<Point>>>,
2183 title: String,
2184 split: bool,
2185 allow_preview: bool,
2186 multibuffer_selection_mode: MultibufferSelectionMode,
2187 window: &mut Window,
2188 cx: &mut Context<Workspace>,
2189 ) -> Option<(Entity<Editor>, Entity<Pane>)> {
2190 if locations.is_empty() {
2191 log::error!("bug: open_locations_in_multibuffer called with empty list of locations");
2192 return None;
2193 }
2194
2195 let capability = workspace.project().read(cx).capability();
2196 let mut ranges = <Vec<Range<Anchor>>>::new();
2197
2198 // a key to find existing multibuffer editors with the same set of locations
2199 // to prevent us from opening more and more multibuffer tabs for searches and the like
2200 let mut key = (title.clone(), vec![]);
2201 let excerpt_buffer = cx.new(|cx| {
2202 let key = &mut key.1;
2203 let mut multibuffer = MultiBuffer::new(capability);
2204 for (buffer, mut ranges_for_buffer) in locations {
2205 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
2206 key.push((buffer.read(cx).remote_id(), ranges_for_buffer.clone()));
2207 multibuffer.set_excerpts_for_path(
2208 PathKey::for_buffer(&buffer, cx),
2209 buffer.clone(),
2210 ranges_for_buffer.clone(),
2211 multibuffer_context_lines(cx),
2212 cx,
2213 );
2214 let snapshot = multibuffer.snapshot(cx);
2215 let buffer_snapshot = buffer.read(cx).snapshot();
2216 ranges.extend(ranges_for_buffer.into_iter().filter_map(|range| {
2217 let text_range = buffer_snapshot.anchor_range_inside(range);
2218 let start = snapshot.anchor_in_buffer(text_range.start)?;
2219 let end = snapshot.anchor_in_buffer(text_range.end)?;
2220 Some(start..end)
2221 }))
2222 }
2223
2224 let final_snapshot = multibuffer.snapshot(cx);
2225 ranges.sort_by(|a, b| a.start.cmp(&b.start, &final_snapshot));
2226
2227 multibuffer.with_title(title)
2228 });
2229 let existing = workspace.active_pane().update(cx, |pane, cx| {
2230 pane.items()
2231 .filter_map(|item| item.downcast::<Editor>())
2232 .find(|editor| {
2233 editor
2234 .read(cx)
2235 .lookup_key
2236 .as_ref()
2237 .and_then(|it| {
2238 it.downcast_ref::<(String, Vec<(BufferId, Vec<Range<Point>>)>)>()
2239 })
2240 .is_some_and(|it| *it == key)
2241 })
2242 });
2243 let was_existing = existing.is_some();
2244 let editor = existing.unwrap_or_else(|| {
2245 cx.new(|cx| {
2246 let mut editor = Editor::for_multibuffer(
2247 excerpt_buffer,
2248 Some(workspace.project().clone()),
2249 window,
2250 cx,
2251 );
2252 editor.lookup_key = Some(Box::new(key));
2253 editor
2254 })
2255 });
2256 editor.update(cx, |editor, cx| match multibuffer_selection_mode {
2257 MultibufferSelectionMode::First => {
2258 if let Some(first_range) = ranges.first() {
2259 editor.change_selections(
2260 SelectionEffects::no_scroll(),
2261 window,
2262 cx,
2263 |selections| {
2264 selections.clear_disjoint();
2265 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
2266 },
2267 );
2268 }
2269 editor.highlight_background(
2270 HighlightKey::Editor,
2271 &ranges,
2272 |_, theme| theme.colors().editor_highlighted_line_background,
2273 cx,
2274 );
2275 }
2276 MultibufferSelectionMode::All => {
2277 editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
2278 selections.clear_disjoint();
2279 selections.select_anchor_ranges(ranges);
2280 });
2281 }
2282 });
2283
2284 let item = Box::new(editor.clone());
2285
2286 let pane = if split {
2287 workspace.adjacent_pane(window, cx)
2288 } else {
2289 workspace.active_pane().clone()
2290 };
2291 let activate_pane = split;
2292
2293 let mut destination_index = None;
2294 pane.update(cx, |pane, cx| {
2295 if allow_preview && !was_existing {
2296 destination_index = pane.replace_preview_item_id(item.item_id(), window, cx);
2297 editor.update(cx, |editor, cx| {
2298 editor
2299 .buffer
2300 .update(cx, |buffer, cx| buffer.refresh_preview(cx))
2301 });
2302 }
2303 if was_existing && !allow_preview {
2304 pane.unpreview_item_if_preview(item.item_id());
2305 }
2306 pane.add_item(item, activate_pane, true, destination_index, window, cx);
2307 });
2308
2309 Some((editor, pane))
2310 }
2311
2312 fn navigation_data(&self, cursor_anchor: Anchor, cx: &mut Context<Self>) -> NavigationData {
2313 let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2314 let buffer = self.buffer.read(cx).read(cx);
2315 let cursor_position = cursor_anchor.to_point(&buffer);
2316 let scroll_anchor = self.scroll_manager.native_anchor(&display_snapshot, cx);
2317 let scroll_top_row = scroll_anchor.top_row(&buffer);
2318 drop(buffer);
2319
2320 NavigationData {
2321 cursor_anchor,
2322 cursor_position,
2323 scroll_anchor,
2324 scroll_top_row,
2325 }
2326 }
2327
2328 fn expand_excerpts_for_direction(
2329 &mut self,
2330 lines: u32,
2331 direction: ExpandExcerptDirection,
2332 cx: &mut Context<Self>,
2333 ) {
2334 let selections = self.selections.disjoint_anchors_arc();
2335
2336 let lines = if lines == 0 {
2337 EditorSettings::get_global(cx).expand_excerpt_lines
2338 } else {
2339 lines
2340 };
2341
2342 let snapshot = self.buffer.read(cx).snapshot(cx);
2343 let excerpt_anchors = selections
2344 .iter()
2345 .flat_map(|selection| {
2346 snapshot
2347 .range_to_buffer_ranges(selection.range())
2348 .into_iter()
2349 .filter_map(|(buffer_snapshot, range, _)| {
2350 snapshot.anchor_in_excerpt(buffer_snapshot.anchor_after(range.start))
2351 })
2352 })
2353 .collect::<Vec<_>>();
2354
2355 if self.delegate_expand_excerpts {
2356 cx.emit(EditorEvent::ExpandExcerptsRequested {
2357 excerpt_anchors,
2358 lines,
2359 direction,
2360 });
2361 return;
2362 }
2363
2364 self.buffer.update(cx, |buffer, cx| {
2365 buffer.expand_excerpts(excerpt_anchors, lines, direction, cx)
2366 })
2367 }
2368
2369 fn go_to_definition_of_kind(
2370 &mut self,
2371 kind: GotoDefinitionKind,
2372 split: bool,
2373 window: &mut Window,
2374 cx: &mut Context<Self>,
2375 ) -> Task<Result<Navigated>> {
2376 let Some(provider) = self.semantics_provider.clone() else {
2377 return Task::ready(Ok(Navigated::No));
2378 };
2379 let head = self
2380 .selections
2381 .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
2382 .head();
2383 let buffer = self.buffer.read(cx);
2384 let Some((buffer, head)) = buffer.text_anchor_for_position(head, cx) else {
2385 return Task::ready(Ok(Navigated::No));
2386 };
2387 let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
2388 return Task::ready(Ok(Navigated::No));
2389 };
2390
2391 let nav_entry = self.navigation_entry(self.selections.newest_anchor().head(), cx);
2392
2393 cx.spawn_in(window, async move |editor, cx| {
2394 let Some(definitions) = definitions.await? else {
2395 return Ok(Navigated::No);
2396 };
2397 let navigated = editor
2398 .update_in(cx, |editor, window, cx| {
2399 editor.navigate_to_hover_links(
2400 Some(kind),
2401 definitions
2402 .into_iter()
2403 .filter(|location| {
2404 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
2405 })
2406 .map(HoverLink::Text)
2407 .collect::<Vec<_>>(),
2408 nav_entry,
2409 split,
2410 window,
2411 cx,
2412 )
2413 })?
2414 .await?;
2415 anyhow::Ok(navigated)
2416 })
2417 }
2418
2419 fn compute_target_location(
2420 &self,
2421 lsp_location: lsp::Location,
2422 server_id: LanguageServerId,
2423 window: &mut Window,
2424 cx: &mut Context<Self>,
2425 ) -> Task<anyhow::Result<Option<Location>>> {
2426 let Some(project) = self.project.clone() else {
2427 return Task::ready(Ok(None));
2428 };
2429
2430 cx.spawn_in(window, async move |editor, cx| {
2431 let location_task = editor.update(cx, |_, cx| {
2432 project.update(cx, |project, cx| {
2433 project.open_local_buffer_via_lsp(lsp_location.uri.clone(), server_id, cx)
2434 })
2435 })?;
2436 let location = Some({
2437 let target_buffer_handle = location_task.await.context("open local buffer")?;
2438 let range = target_buffer_handle.read_with(cx, |target_buffer, _| {
2439 let target_start = target_buffer
2440 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
2441 let target_end = target_buffer
2442 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
2443 target_buffer.anchor_after(target_start)
2444 ..target_buffer.anchor_before(target_end)
2445 });
2446 Location {
2447 buffer: target_buffer_handle,
2448 range,
2449 }
2450 });
2451 Ok(location)
2452 })
2453 }
2454
2455 fn go_to_singleton_buffer_range_impl(
2456 &mut self,
2457 range: Range<Point>,
2458 record_nav_history: bool,
2459 window: &mut Window,
2460 cx: &mut Context<Self>,
2461 ) {
2462 let multibuffer = self.buffer().read(cx);
2463 let Some(buffer) = multibuffer.as_singleton() else {
2464 return;
2465 };
2466 let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
2467 return;
2468 };
2469 let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
2470 return;
2471 };
2472 self.change_selections(
2473 SelectionEffects::scroll(Autoscroll::for_go_to_definition(
2474 self.cursor_top_offset(cx),
2475 cx,
2476 ))
2477 .nav_history(record_nav_history),
2478 window,
2479 cx,
2480 |s| s.select_anchor_ranges([start..end]),
2481 );
2482 }
2483
2484 fn go_to_document_highlight_before_or_after_position(
2485 &mut self,
2486 direction: Direction,
2487 window: &mut Window,
2488 cx: &mut Context<Editor>,
2489 ) {
2490 let snapshot = self.snapshot(window, cx);
2491 let buffer = &snapshot.buffer_snapshot();
2492 let position = self
2493 .selections
2494 .newest::<Point>(&snapshot.display_snapshot)
2495 .head();
2496 let anchor_position = buffer.anchor_after(position);
2497
2498 // Get all document highlights (both read and write)
2499 let mut all_highlights = Vec::new();
2500
2501 if let Some((_, read_highlights)) = self
2502 .background_highlights
2503 .get(&HighlightKey::DocumentHighlightRead)
2504 {
2505 all_highlights.extend(read_highlights.iter());
2506 }
2507
2508 if let Some((_, write_highlights)) = self
2509 .background_highlights
2510 .get(&HighlightKey::DocumentHighlightWrite)
2511 {
2512 all_highlights.extend(write_highlights.iter());
2513 }
2514
2515 if all_highlights.is_empty() {
2516 return;
2517 }
2518
2519 // Sort highlights by position
2520 all_highlights.sort_by(|a, b| a.start.cmp(&b.start, buffer));
2521
2522 let target_highlight = match direction {
2523 Direction::Next => {
2524 // Find the first highlight after the current position
2525 all_highlights
2526 .iter()
2527 .find(|highlight| highlight.start.cmp(&anchor_position, buffer).is_gt())
2528 }
2529 Direction::Prev => {
2530 // Find the last highlight before the current position
2531 all_highlights
2532 .iter()
2533 .rev()
2534 .find(|highlight| highlight.end.cmp(&anchor_position, buffer).is_lt())
2535 }
2536 };
2537
2538 if let Some(highlight) = target_highlight {
2539 let destination = highlight.start.to_point(buffer);
2540 let autoscroll = Autoscroll::center();
2541
2542 self.unfold_ranges(&[destination..destination], false, false, cx);
2543 self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
2544 s.select_ranges([destination..destination]);
2545 });
2546 }
2547 }
2548}
2549