Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T01:33:00.671Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

navigation.rs

2549 lines · 93.5 KB · rust
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
Served at tenant.openagents/omega Member data and write actions are omitted.