1#![allow(rustdoc::private_intra_doc_links)]
2//! This is the place where everything editor-related is stored (data-wise) and displayed (ui-wise).
3//! The main point of interest in this crate is [`Editor`] type, which is used in every other Zed part as a user input element.
4//! It comes in different flavors: single line, multiline and a fixed height one.
5//!
6//! Editor contains of multiple large submodules:
7//! * [`element`] — the place where all rendering happens
8//! * [`display_map`] - chunks up text in the editor into the logical blocks, establishes coordinates and mapping between each of them.
9//! Contains all metadata related to text transformations (folds, fake inlay text insertions, soft wraps, tab markup, etc.).
10//! * [`inlay_hint_cache`] - is a storage of inlay hints out of LSP requests, responsible for querying LSP and updating `display_map`'s state accordingly.
11//!
12//! All other submodules and structs are mostly concerned with holding editor data about the way it displays current buffer region(s).
13//!
14//! If you're looking to improve Vim mode, you should check out Vim crate that wraps Editor and overrides it's behaviour.
15pub mod actions;
16mod blink_manager;
17pub mod display_map;
18mod editor_settings;
19mod element;
20mod inlay_hint_cache;
21
22mod git;
23mod highlight_matching_bracket;
24mod hover_popover;
25pub mod items;
26mod link_go_to_definition;
27mod mouse_context_menu;
28pub mod movement;
29mod persistence;
30mod rust_analyzer_ext;
31pub mod scroll;
32mod selections_collection;
33
34#[cfg(test)]
35mod editor_tests;
36#[cfg(any(test, feature = "test-support"))]
37pub mod test;
38use ::git::diff::DiffHunk;
39pub(crate) use actions::*;
40use aho_corasick::AhoCorasick;
41use anyhow::{anyhow, Context as _, Result};
42use blink_manager::BlinkManager;
43use client::{Collaborator, ParticipantIndex};
44use clock::ReplicaId;
45use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
46use convert_case::{Case, Casing};
47use copilot::Copilot;
48pub use display_map::DisplayPoint;
49use display_map::*;
50pub use editor_settings::EditorSettings;
51use element::LineWithInvisibles;
52pub use element::{Cursor, EditorElement, HighlightedRange, HighlightedRangeLine};
53use futures::FutureExt;
54use fuzzy::{StringMatch, StringMatchCandidate};
55use git::diff_hunk_to_display;
56use gpui::{
57 div, impl_actions, point, prelude::*, px, relative, rems, size, uniform_list, Action,
58 AnyElement, AppContext, AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem, Context,
59 DispatchPhase, ElementId, EventEmitter, FocusHandle, FocusableView, FontStyle, FontWeight,
60 HighlightStyle, Hsla, InputHandler, InteractiveText, KeyContext, Model, MouseButton,
61 ParentElement, Pixels, Render, SharedString, Styled, StyledText, Subscription, Task, TextStyle,
62 UniformListScrollHandle, View, ViewContext, VisualContext, WeakView, WhiteSpace, WindowContext,
63};
64use highlight_matching_bracket::refresh_matching_bracket_highlights;
65use hover_popover::{hide_hover, HoverState};
66use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
67pub use items::MAX_TAB_TITLE_LEN;
68use itertools::Itertools;
69use language::{char_kind, CharKind};
70use language::{
71 language_settings::{self, all_language_settings, InlayHintSettings},
72 markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, Capability, CodeAction,
73 CodeLabel, Completion, CursorShape, Diagnostic, Documentation, IndentKind, IndentSize,
74 Language, LanguageServerName, OffsetRangeExt, Point, Selection, SelectionGoal, TransactionId,
75};
76
77use link_go_to_definition::{GoToDefinitionLink, InlayHighlight, LinkGoToDefinitionState};
78use lsp::{DiagnosticSeverity, LanguageServerId};
79use mouse_context_menu::MouseContextMenu;
80use movement::TextLayoutDetails;
81use multi_buffer::ToOffsetUtf16;
82pub use multi_buffer::{
83 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
84 ToPoint,
85};
86use ordered_float::OrderedFloat;
87use parking_lot::RwLock;
88use project::{FormatTrigger, Location, Project, ProjectPath, ProjectTransaction};
89use rand::prelude::*;
90use rpc::proto::*;
91use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
92use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection};
93use serde::{Deserialize, Serialize};
94use settings::{Settings, SettingsStore};
95use smallvec::SmallVec;
96use snippet::Snippet;
97use std::{
98 any::TypeId,
99 borrow::Cow,
100 cmp::{self, Ordering, Reverse},
101 mem,
102 num::NonZeroU32,
103 ops::{ControlFlow, Deref, DerefMut, Range, RangeInclusive},
104 path::Path,
105 sync::Arc,
106 sync::Weak,
107 time::{Duration, Instant},
108};
109pub use sum_tree::Bias;
110use sum_tree::TreeMap;
111use text::{OffsetUtf16, Rope};
112use theme::{
113 observe_buffer_font_size_adjustment, ActiveTheme, PlayerColor, StatusColors, SyntaxTheme,
114 ThemeColors, ThemeSettings,
115};
116use ui::{
117 h_flex, prelude::*, ButtonSize, ButtonStyle, IconButton, IconName, IconSize, ListItem, Popover,
118 Tooltip,
119};
120use util::{post_inc, RangeExt, ResultExt, TryFutureExt};
121use workspace::{searchable::SearchEvent, ItemNavHistory, Pane, SplitDirection, ViewId, Workspace};
122
123const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
124const MAX_LINE_LEN: usize = 1024;
125const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
126const MAX_SELECTION_HISTORY_LEN: usize = 1024;
127const COPILOT_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
128#[doc(hidden)]
129pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
130#[doc(hidden)]
131pub const DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
132
133pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
134
135pub fn render_parsed_markdown(
136 element_id: impl Into<ElementId>,
137 parsed: &language::ParsedMarkdown,
138 editor_style: &EditorStyle,
139 workspace: Option<WeakView<Workspace>>,
140 cx: &mut ViewContext<Editor>,
141) -> InteractiveText {
142 let code_span_background_color = cx
143 .theme()
144 .colors()
145 .editor_document_highlight_read_background;
146
147 let highlights = gpui::combine_highlights(
148 parsed.highlights.iter().filter_map(|(range, highlight)| {
149 let highlight = highlight.to_highlight_style(&editor_style.syntax)?;
150 Some((range.clone(), highlight))
151 }),
152 parsed
153 .regions
154 .iter()
155 .zip(&parsed.region_ranges)
156 .filter_map(|(region, range)| {
157 if region.code {
158 Some((
159 range.clone(),
160 HighlightStyle {
161 background_color: Some(code_span_background_color),
162 ..Default::default()
163 },
164 ))
165 } else {
166 None
167 }
168 }),
169 );
170
171 let mut links = Vec::new();
172 let mut link_ranges = Vec::new();
173 for (range, region) in parsed.region_ranges.iter().zip(&parsed.regions) {
174 if let Some(link) = region.link.clone() {
175 links.push(link);
176 link_ranges.push(range.clone());
177 }
178 }
179
180 InteractiveText::new(
181 element_id,
182 StyledText::new(parsed.text.clone()).with_highlights(&editor_style.text, highlights),
183 )
184 .on_click(link_ranges, move |clicked_range_ix, cx| {
185 match &links[clicked_range_ix] {
186 markdown::Link::Web { url } => cx.open_url(url),
187 markdown::Link::Path { path } => {
188 if let Some(workspace) = &workspace {
189 _ = workspace.update(cx, |workspace, cx| {
190 workspace.open_abs_path(path.clone(), false, cx).detach();
191 });
192 }
193 }
194 }
195 })
196}
197
198#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
199pub(crate) enum InlayId {
200 Suggestion(usize),
201 Hint(usize),
202}
203
204impl InlayId {
205 fn id(&self) -> usize {
206 match self {
207 Self::Suggestion(id) => *id,
208 Self::Hint(id) => *id,
209 }
210 }
211}
212
213enum DocumentHighlightRead {}
214enum DocumentHighlightWrite {}
215enum InputComposition {}
216
217#[derive(Copy, Clone, PartialEq, Eq)]
218pub enum Direction {
219 Prev,
220 Next,
221}
222
223pub fn init_settings(cx: &mut AppContext) {
224 EditorSettings::register(cx);
225}
226
227pub fn init(cx: &mut AppContext) {
228 init_settings(cx);
229
230 workspace::register_project_item::<Editor>(cx);
231 workspace::register_followable_item::<Editor>(cx);
232 workspace::register_deserializable_item::<Editor>(cx);
233 cx.observe_new_views(
234 |workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>| {
235 workspace.register_action(Editor::new_file);
236 workspace.register_action(Editor::new_file_in_direction);
237 },
238 )
239 .detach();
240
241 cx.on_action(move |_: &workspace::NewFile, cx| {
242 let app_state = cx.global::<Weak<workspace::AppState>>();
243 if let Some(app_state) = app_state.upgrade() {
244 workspace::open_new(&app_state, cx, |workspace, cx| {
245 Editor::new_file(workspace, &Default::default(), cx)
246 })
247 .detach();
248 }
249 });
250 cx.on_action(move |_: &workspace::NewWindow, cx| {
251 let app_state = cx.global::<Weak<workspace::AppState>>();
252 if let Some(app_state) = app_state.upgrade() {
253 workspace::open_new(&app_state, cx, |workspace, cx| {
254 Editor::new_file(workspace, &Default::default(), cx)
255 })
256 .detach();
257 }
258 });
259}
260
261trait InvalidationRegion {
262 fn ranges(&self) -> &[Range<Anchor>];
263}
264
265#[derive(Clone, Debug, PartialEq)]
266pub enum SelectPhase {
267 Begin {
268 position: DisplayPoint,
269 add: bool,
270 click_count: usize,
271 },
272 BeginColumnar {
273 position: DisplayPoint,
274 goal_column: u32,
275 },
276 Extend {
277 position: DisplayPoint,
278 click_count: usize,
279 },
280 Update {
281 position: DisplayPoint,
282 goal_column: u32,
283 scroll_position: gpui::Point<f32>,
284 },
285 End,
286}
287
288#[derive(Clone, Debug)]
289pub(crate) enum SelectMode {
290 Character,
291 Word(Range<Anchor>),
292 Line(Range<Anchor>),
293 All,
294}
295
296#[derive(Copy, Clone, PartialEq, Eq, Debug)]
297pub enum EditorMode {
298 SingleLine,
299 AutoHeight { max_lines: usize },
300 Full,
301}
302
303#[derive(Clone, Debug)]
304pub enum SoftWrap {
305 None,
306 EditorWidth,
307 Column(u32),
308}
309
310#[derive(Clone)]
311pub struct EditorStyle {
312 pub background: Hsla,
313 pub local_player: PlayerColor,
314 pub text: TextStyle,
315 pub scrollbar_width: Pixels,
316 pub syntax: Arc<SyntaxTheme>,
317 pub status: StatusColors,
318 pub inlays_style: HighlightStyle,
319 pub suggestions_style: HighlightStyle,
320}
321
322impl Default for EditorStyle {
323 fn default() -> Self {
324 Self {
325 background: Hsla::default(),
326 local_player: PlayerColor::default(),
327 text: TextStyle::default(),
328 scrollbar_width: Pixels::default(),
329 syntax: Default::default(),
330 // HACK: Status colors don't have a real default.
331 // We should look into removing the status colors from the editor
332 // style and retrieve them directly from the theme.
333 status: StatusColors::dark(),
334 inlays_style: HighlightStyle::default(),
335 suggestions_style: HighlightStyle::default(),
336 }
337 }
338}
339
340type CompletionId = usize;
341
342// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
343// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
344
345type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Vec<Range<Anchor>>);
346type InlayBackgroundHighlight = (fn(&ThemeColors) -> Hsla, Vec<InlayHighlight>);
347
348pub struct Editor {
349 handle: WeakView<Self>,
350 focus_handle: FocusHandle,
351 buffer: Model<MultiBuffer>,
352 display_map: Model<DisplayMap>,
353 pub selections: SelectionsCollection,
354 pub scroll_manager: ScrollManager,
355 columnar_selection_tail: Option<Anchor>,
356 add_selections_state: Option<AddSelectionsState>,
357 select_next_state: Option<SelectNextState>,
358 select_prev_state: Option<SelectNextState>,
359 selection_history: SelectionHistory,
360 autoclose_regions: Vec<AutocloseRegion>,
361 snippet_stack: InvalidationStack<SnippetState>,
362 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
363 ime_transaction: Option<TransactionId>,
364 active_diagnostics: Option<ActiveDiagnosticGroup>,
365 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
366 project: Option<Model<Project>>,
367 completion_provider: Option<Box<dyn CompletionProvider>>,
368 collaboration_hub: Option<Box<dyn CollaborationHub>>,
369 blink_manager: Model<BlinkManager>,
370 show_cursor_names: bool,
371 hovered_cursor: Option<HoveredCursor>,
372 pub show_local_selections: bool,
373 mode: EditorMode,
374 show_gutter: bool,
375 show_wrap_guides: Option<bool>,
376 placeholder_text: Option<Arc<str>>,
377 highlighted_rows: Option<Range<u32>>,
378 background_highlights: BTreeMap<TypeId, BackgroundHighlight>,
379 inlay_background_highlights: TreeMap<Option<TypeId>, InlayBackgroundHighlight>,
380 nav_history: Option<ItemNavHistory>,
381 context_menu: RwLock<Option<ContextMenu>>,
382 mouse_context_menu: Option<MouseContextMenu>,
383 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
384 next_completion_id: CompletionId,
385 available_code_actions: Option<(Model<Buffer>, Arc<[CodeAction]>)>,
386 code_actions_task: Option<Task<()>>,
387 document_highlights_task: Option<Task<()>>,
388 pending_rename: Option<RenameState>,
389 searchable: bool,
390 cursor_shape: CursorShape,
391 collapse_matches: bool,
392 autoindent_mode: Option<AutoindentMode>,
393 workspace: Option<(WeakView<Workspace>, i64)>,
394 keymap_context_layers: BTreeMap<TypeId, KeyContext>,
395 input_enabled: bool,
396 read_only: bool,
397 leader_peer_id: Option<PeerId>,
398 remote_id: Option<ViewId>,
399 hover_state: HoverState,
400 gutter_hovered: bool,
401 link_go_to_definition_state: LinkGoToDefinitionState,
402 copilot_state: CopilotState,
403 inlay_hint_cache: InlayHintCache,
404 next_inlay_id: usize,
405 _subscriptions: Vec<Subscription>,
406 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
407 gutter_width: Pixels,
408 style: Option<EditorStyle>,
409 editor_actions: Vec<Box<dyn Fn(&mut ViewContext<Self>)>>,
410 show_copilot_suggestions: bool,
411}
412
413pub struct EditorSnapshot {
414 pub mode: EditorMode,
415 pub show_gutter: bool,
416 pub display_snapshot: DisplaySnapshot,
417 pub placeholder_text: Option<Arc<str>>,
418 is_focused: bool,
419 scroll_anchor: ScrollAnchor,
420 ongoing_scroll: OngoingScroll,
421}
422
423#[derive(Debug)]
424pub struct RemoteSelection {
425 pub replica_id: ReplicaId,
426 pub selection: Selection<Anchor>,
427 pub cursor_shape: CursorShape,
428 pub peer_id: PeerId,
429 pub line_mode: bool,
430 pub participant_index: Option<ParticipantIndex>,
431 pub user_name: Option<SharedString>,
432}
433
434#[derive(Clone, Debug)]
435struct SelectionHistoryEntry {
436 selections: Arc<[Selection<Anchor>]>,
437 select_next_state: Option<SelectNextState>,
438 select_prev_state: Option<SelectNextState>,
439 add_selections_state: Option<AddSelectionsState>,
440}
441
442enum SelectionHistoryMode {
443 Normal,
444 Undoing,
445 Redoing,
446}
447
448struct HoveredCursor {
449 replica_id: u16,
450 selection_id: usize,
451}
452
453impl Default for SelectionHistoryMode {
454 fn default() -> Self {
455 Self::Normal
456 }
457}
458
459#[derive(Default)]
460struct SelectionHistory {
461 #[allow(clippy::type_complexity)]
462 selections_by_transaction:
463 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
464 mode: SelectionHistoryMode,
465 undo_stack: VecDeque<SelectionHistoryEntry>,
466 redo_stack: VecDeque<SelectionHistoryEntry>,
467}
468
469impl SelectionHistory {
470 fn insert_transaction(
471 &mut self,
472 transaction_id: TransactionId,
473 selections: Arc<[Selection<Anchor>]>,
474 ) {
475 self.selections_by_transaction
476 .insert(transaction_id, (selections, None));
477 }
478
479 #[allow(clippy::type_complexity)]
480 fn transaction(
481 &self,
482 transaction_id: TransactionId,
483 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
484 self.selections_by_transaction.get(&transaction_id)
485 }
486
487 #[allow(clippy::type_complexity)]
488 fn transaction_mut(
489 &mut self,
490 transaction_id: TransactionId,
491 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
492 self.selections_by_transaction.get_mut(&transaction_id)
493 }
494
495 fn push(&mut self, entry: SelectionHistoryEntry) {
496 if !entry.selections.is_empty() {
497 match self.mode {
498 SelectionHistoryMode::Normal => {
499 self.push_undo(entry);
500 self.redo_stack.clear();
501 }
502 SelectionHistoryMode::Undoing => self.push_redo(entry),
503 SelectionHistoryMode::Redoing => self.push_undo(entry),
504 }
505 }
506 }
507
508 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
509 if self
510 .undo_stack
511 .back()
512 .map_or(true, |e| e.selections != entry.selections)
513 {
514 self.undo_stack.push_back(entry);
515 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
516 self.undo_stack.pop_front();
517 }
518 }
519 }
520
521 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
522 if self
523 .redo_stack
524 .back()
525 .map_or(true, |e| e.selections != entry.selections)
526 {
527 self.redo_stack.push_back(entry);
528 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
529 self.redo_stack.pop_front();
530 }
531 }
532 }
533}
534
535#[derive(Clone, Debug)]
536struct AddSelectionsState {
537 above: bool,
538 stack: Vec<usize>,
539}
540
541#[derive(Clone)]
542struct SelectNextState {
543 query: AhoCorasick,
544 wordwise: bool,
545 done: bool,
546}
547
548impl std::fmt::Debug for SelectNextState {
549 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
550 f.debug_struct(std::any::type_name::<Self>())
551 .field("wordwise", &self.wordwise)
552 .field("done", &self.done)
553 .finish()
554 }
555}
556
557#[derive(Debug)]
558struct AutocloseRegion {
559 selection_id: usize,
560 range: Range<Anchor>,
561 pair: BracketPair,
562}
563
564#[derive(Debug)]
565struct SnippetState {
566 ranges: Vec<Vec<Range<Anchor>>>,
567 active_index: usize,
568}
569
570#[doc(hidden)]
571pub struct RenameState {
572 pub range: Range<Anchor>,
573 pub old_name: Arc<str>,
574 pub editor: View<Editor>,
575 block_id: BlockId,
576}
577
578struct InvalidationStack<T>(Vec<T>);
579
580enum ContextMenu {
581 Completions(CompletionsMenu),
582 CodeActions(CodeActionsMenu),
583}
584
585impl ContextMenu {
586 fn select_first(
587 &mut self,
588 project: Option<&Model<Project>>,
589 cx: &mut ViewContext<Editor>,
590 ) -> bool {
591 if self.visible() {
592 match self {
593 ContextMenu::Completions(menu) => menu.select_first(project, cx),
594 ContextMenu::CodeActions(menu) => menu.select_first(cx),
595 }
596 true
597 } else {
598 false
599 }
600 }
601
602 fn select_prev(
603 &mut self,
604 project: Option<&Model<Project>>,
605 cx: &mut ViewContext<Editor>,
606 ) -> bool {
607 if self.visible() {
608 match self {
609 ContextMenu::Completions(menu) => menu.select_prev(project, cx),
610 ContextMenu::CodeActions(menu) => menu.select_prev(cx),
611 }
612 true
613 } else {
614 false
615 }
616 }
617
618 fn select_next(
619 &mut self,
620 project: Option<&Model<Project>>,
621 cx: &mut ViewContext<Editor>,
622 ) -> bool {
623 if self.visible() {
624 match self {
625 ContextMenu::Completions(menu) => menu.select_next(project, cx),
626 ContextMenu::CodeActions(menu) => menu.select_next(cx),
627 }
628 true
629 } else {
630 false
631 }
632 }
633
634 fn select_last(
635 &mut self,
636 project: Option<&Model<Project>>,
637 cx: &mut ViewContext<Editor>,
638 ) -> bool {
639 if self.visible() {
640 match self {
641 ContextMenu::Completions(menu) => menu.select_last(project, cx),
642 ContextMenu::CodeActions(menu) => menu.select_last(cx),
643 }
644 true
645 } else {
646 false
647 }
648 }
649
650 fn visible(&self) -> bool {
651 match self {
652 ContextMenu::Completions(menu) => menu.visible(),
653 ContextMenu::CodeActions(menu) => menu.visible(),
654 }
655 }
656
657 fn render(
658 &self,
659 cursor_position: DisplayPoint,
660 style: &EditorStyle,
661 max_height: Pixels,
662 workspace: Option<WeakView<Workspace>>,
663 cx: &mut ViewContext<Editor>,
664 ) -> (DisplayPoint, AnyElement) {
665 match self {
666 ContextMenu::Completions(menu) => (
667 cursor_position,
668 menu.render(style, max_height, workspace, cx),
669 ),
670 ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, max_height, cx),
671 }
672 }
673}
674
675#[derive(Clone)]
676struct CompletionsMenu {
677 id: CompletionId,
678 initial_position: Anchor,
679 buffer: Model<Buffer>,
680 completions: Arc<RwLock<Box<[Completion]>>>,
681 match_candidates: Arc<[StringMatchCandidate]>,
682 matches: Arc<[StringMatch]>,
683 selected_item: usize,
684 scroll_handle: UniformListScrollHandle,
685}
686
687impl CompletionsMenu {
688 fn select_first(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
689 self.selected_item = 0;
690 self.scroll_handle.scroll_to_item(self.selected_item);
691 self.attempt_resolve_selected_completion_documentation(project, cx);
692 cx.notify();
693 }
694
695 fn select_prev(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
696 if self.selected_item > 0 {
697 self.selected_item -= 1;
698 } else {
699 self.selected_item = self.matches.len() - 1;
700 }
701 self.scroll_handle.scroll_to_item(self.selected_item);
702 self.attempt_resolve_selected_completion_documentation(project, cx);
703 cx.notify();
704 }
705
706 fn select_next(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
707 if self.selected_item + 1 < self.matches.len() {
708 self.selected_item += 1;
709 } else {
710 self.selected_item = 0;
711 }
712 self.scroll_handle.scroll_to_item(self.selected_item);
713 self.attempt_resolve_selected_completion_documentation(project, cx);
714 cx.notify();
715 }
716
717 fn select_last(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
718 self.selected_item = self.matches.len() - 1;
719 self.scroll_handle.scroll_to_item(self.selected_item);
720 self.attempt_resolve_selected_completion_documentation(project, cx);
721 cx.notify();
722 }
723
724 fn pre_resolve_completion_documentation(
725 &self,
726 editor: &Editor,
727 cx: &mut ViewContext<Editor>,
728 ) -> Option<Task<()>> {
729 let settings = EditorSettings::get_global(cx);
730 if !settings.show_completion_documentation {
731 return None;
732 }
733
734 let Some(provider) = editor.completion_provider.as_ref() else {
735 return None;
736 };
737
738 let resolve_task = provider.resolve_completions(
739 self.matches.iter().map(|m| m.candidate_id).collect(),
740 self.completions.clone(),
741 cx,
742 );
743
744 return Some(cx.spawn(move |this, mut cx| async move {
745 if let Some(true) = resolve_task.await.log_err() {
746 this.update(&mut cx, |_, cx| cx.notify()).ok();
747 }
748 }));
749 }
750
751 fn attempt_resolve_selected_completion_documentation(
752 &mut self,
753 project: Option<&Model<Project>>,
754 cx: &mut ViewContext<Editor>,
755 ) {
756 let settings = EditorSettings::get_global(cx);
757 if !settings.show_completion_documentation {
758 return;
759 }
760
761 let completion_index = self.matches[self.selected_item].candidate_id;
762 let Some(project) = project else {
763 return;
764 };
765
766 let resolve_task = project.update(cx, |project, cx| {
767 project.resolve_completions(vec![completion_index], self.completions.clone(), cx)
768 });
769 cx.spawn(move |this, mut cx| async move {
770 if let Some(true) = resolve_task.await.log_err() {
771 this.update(&mut cx, |_, cx| cx.notify()).ok();
772 }
773 })
774 .detach();
775 }
776
777 fn visible(&self) -> bool {
778 !self.matches.is_empty()
779 }
780
781 fn render(
782 &self,
783 style: &EditorStyle,
784 max_height: Pixels,
785 workspace: Option<WeakView<Workspace>>,
786 cx: &mut ViewContext<Editor>,
787 ) -> AnyElement {
788 let settings = EditorSettings::get_global(cx);
789 let show_completion_documentation = settings.show_completion_documentation;
790
791 let widest_completion_ix = self
792 .matches
793 .iter()
794 .enumerate()
795 .max_by_key(|(_, mat)| {
796 let completions = self.completions.read();
797 let completion = &completions[mat.candidate_id];
798 let documentation = &completion.documentation;
799
800 let mut len = completion.label.text.chars().count();
801 if let Some(Documentation::SingleLine(text)) = documentation {
802 if show_completion_documentation {
803 len += text.chars().count();
804 }
805 }
806
807 len
808 })
809 .map(|(ix, _)| ix);
810
811 let completions = self.completions.clone();
812 let matches = self.matches.clone();
813 let selected_item = self.selected_item;
814 let style = style.clone();
815
816 let multiline_docs = {
817 let mat = &self.matches[selected_item];
818 let multiline_docs = match &self.completions.read()[mat.candidate_id].documentation {
819 Some(Documentation::MultiLinePlainText(text)) => {
820 Some(div().child(SharedString::from(text.clone())))
821 }
822 Some(Documentation::MultiLineMarkdown(parsed)) => Some(div().child(
823 render_parsed_markdown("completions_markdown", parsed, &style, workspace, cx),
824 )),
825 _ => None,
826 };
827 multiline_docs.map(|div| {
828 div.id("multiline_docs")
829 .max_h(max_height)
830 .flex_1()
831 .px_1p5()
832 .py_1()
833 .min_w(px(260.))
834 .max_w(px(640.))
835 .w(px(500.))
836 .overflow_y_scroll()
837 // Prevent a mouse down on documentation from being propagated to the editor,
838 // because that would move the cursor.
839 .on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
840 })
841 };
842
843 let list = uniform_list(
844 cx.view().clone(),
845 "completions",
846 matches.len(),
847 move |_editor, range, cx| {
848 let start_ix = range.start;
849 let completions_guard = completions.read();
850
851 matches[range]
852 .iter()
853 .enumerate()
854 .map(|(ix, mat)| {
855 let item_ix = start_ix + ix;
856 let candidate_id = mat.candidate_id;
857 let completion = &completions_guard[candidate_id];
858
859 let documentation = if show_completion_documentation {
860 &completion.documentation
861 } else {
862 &None
863 };
864
865 let highlights = gpui::combine_highlights(
866 mat.ranges().map(|range| (range, FontWeight::BOLD.into())),
867 styled_runs_for_code_label(&completion.label, &style.syntax).map(
868 |(range, mut highlight)| {
869 // Ignore font weight for syntax highlighting, as we'll use it
870 // for fuzzy matches.
871 highlight.font_weight = None;
872 (range, highlight)
873 },
874 ),
875 );
876 let completion_label = StyledText::new(completion.label.text.clone())
877 .with_highlights(&style.text, highlights);
878 let documentation_label =
879 if let Some(Documentation::SingleLine(text)) = documentation {
880 if text.trim().is_empty() {
881 None
882 } else {
883 Some(
884 h_flex().ml_4().child(
885 Label::new(text.clone())
886 .size(LabelSize::Small)
887 .color(Color::Muted),
888 ),
889 )
890 }
891 } else {
892 None
893 };
894
895 div().min_w(px(220.)).max_w(px(540.)).child(
896 ListItem::new(mat.candidate_id)
897 .inset(true)
898 .selected(item_ix == selected_item)
899 .on_click(cx.listener(move |editor, _event, cx| {
900 cx.stop_propagation();
901 editor
902 .confirm_completion(
903 &ConfirmCompletion {
904 item_ix: Some(item_ix),
905 },
906 cx,
907 )
908 .map(|task| task.detach_and_log_err(cx));
909 }))
910 .child(h_flex().overflow_hidden().child(completion_label))
911 .end_slot::<Div>(documentation_label),
912 )
913 })
914 .collect()
915 },
916 )
917 .max_h(max_height)
918 .track_scroll(self.scroll_handle.clone())
919 .with_width_from_item(widest_completion_ix);
920
921 Popover::new()
922 .child(list)
923 .when_some(multiline_docs, |popover, multiline_docs| {
924 popover.aside(multiline_docs)
925 })
926 .into_any_element()
927 }
928
929 pub async fn filter(&mut self, query: Option<&str>, executor: BackgroundExecutor) {
930 let mut matches = if let Some(query) = query {
931 fuzzy::match_strings(
932 &self.match_candidates,
933 query,
934 query.chars().any(|c| c.is_uppercase()),
935 100,
936 &Default::default(),
937 executor,
938 )
939 .await
940 } else {
941 self.match_candidates
942 .iter()
943 .enumerate()
944 .map(|(candidate_id, candidate)| StringMatch {
945 candidate_id,
946 score: Default::default(),
947 positions: Default::default(),
948 string: candidate.string.clone(),
949 })
950 .collect()
951 };
952
953 // Remove all candidates where the query's start does not match the start of any word in the candidate
954 if let Some(query) = query {
955 if let Some(query_start) = query.chars().next() {
956 matches.retain(|string_match| {
957 split_words(&string_match.string).any(|word| {
958 // Check that the first codepoint of the word as lowercase matches the first
959 // codepoint of the query as lowercase
960 word.chars()
961 .flat_map(|codepoint| codepoint.to_lowercase())
962 .zip(query_start.to_lowercase())
963 .all(|(word_cp, query_cp)| word_cp == query_cp)
964 })
965 });
966 }
967 }
968
969 let completions = self.completions.read();
970 matches.sort_unstable_by_key(|mat| {
971 let completion = &completions[mat.candidate_id];
972 (
973 completion.lsp_completion.sort_text.as_ref(),
974 Reverse(OrderedFloat(mat.score)),
975 completion.sort_key(),
976 )
977 });
978
979 for mat in &mut matches {
980 let completion = &completions[mat.candidate_id];
981 mat.string = completion.label.text.clone();
982 for position in &mut mat.positions {
983 *position += completion.label.filter_range.start;
984 }
985 }
986 drop(completions);
987
988 self.matches = matches.into();
989 self.selected_item = 0;
990 }
991}
992
993#[derive(Clone)]
994struct CodeActionsMenu {
995 actions: Arc<[CodeAction]>,
996 buffer: Model<Buffer>,
997 selected_item: usize,
998 scroll_handle: UniformListScrollHandle,
999 deployed_from_indicator: bool,
1000}
1001
1002impl CodeActionsMenu {
1003 fn select_first(&mut self, cx: &mut ViewContext<Editor>) {
1004 self.selected_item = 0;
1005 self.scroll_handle.scroll_to_item(self.selected_item);
1006 cx.notify()
1007 }
1008
1009 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
1010 if self.selected_item > 0 {
1011 self.selected_item -= 1;
1012 } else {
1013 self.selected_item = self.actions.len() - 1;
1014 }
1015 self.scroll_handle.scroll_to_item(self.selected_item);
1016 cx.notify();
1017 }
1018
1019 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
1020 if self.selected_item + 1 < self.actions.len() {
1021 self.selected_item += 1;
1022 } else {
1023 self.selected_item = 0;
1024 }
1025 self.scroll_handle.scroll_to_item(self.selected_item);
1026 cx.notify();
1027 }
1028
1029 fn select_last(&mut self, cx: &mut ViewContext<Editor>) {
1030 self.selected_item = self.actions.len() - 1;
1031 self.scroll_handle.scroll_to_item(self.selected_item);
1032 cx.notify()
1033 }
1034
1035 fn visible(&self) -> bool {
1036 !self.actions.is_empty()
1037 }
1038
1039 fn render(
1040 &self,
1041 mut cursor_position: DisplayPoint,
1042 _style: &EditorStyle,
1043 max_height: Pixels,
1044 cx: &mut ViewContext<Editor>,
1045 ) -> (DisplayPoint, AnyElement) {
1046 let actions = self.actions.clone();
1047 let selected_item = self.selected_item;
1048
1049 let element = uniform_list(
1050 cx.view().clone(),
1051 "code_actions_menu",
1052 self.actions.len(),
1053 move |_this, range, cx| {
1054 actions[range.clone()]
1055 .iter()
1056 .enumerate()
1057 .map(|(ix, action)| {
1058 let item_ix = range.start + ix;
1059 let selected = selected_item == item_ix;
1060 let colors = cx.theme().colors();
1061 div()
1062 .px_2()
1063 .text_color(colors.text)
1064 .when(selected, |style| {
1065 style
1066 .bg(colors.element_active)
1067 .text_color(colors.text_accent)
1068 })
1069 .hover(|style| {
1070 style
1071 .bg(colors.element_hover)
1072 .text_color(colors.text_accent)
1073 })
1074 .on_mouse_down(
1075 MouseButton::Left,
1076 cx.listener(move |editor, _, cx| {
1077 cx.stop_propagation();
1078 editor
1079 .confirm_code_action(
1080 &ConfirmCodeAction {
1081 item_ix: Some(item_ix),
1082 },
1083 cx,
1084 )
1085 .map(|task| task.detach_and_log_err(cx));
1086 }),
1087 )
1088 // TASK: It would be good to make lsp_action.title a SharedString to avoid allocating here.
1089 .child(SharedString::from(action.lsp_action.title.clone()))
1090 })
1091 .collect()
1092 },
1093 )
1094 .elevation_1(cx)
1095 .px_2()
1096 .py_1()
1097 .max_h(max_height)
1098 .track_scroll(self.scroll_handle.clone())
1099 .with_width_from_item(
1100 self.actions
1101 .iter()
1102 .enumerate()
1103 .max_by_key(|(_, action)| action.lsp_action.title.chars().count())
1104 .map(|(ix, _)| ix),
1105 )
1106 .into_any_element();
1107
1108 if self.deployed_from_indicator {
1109 *cursor_position.column_mut() = 0;
1110 }
1111
1112 (cursor_position, element)
1113 }
1114}
1115
1116pub(crate) struct CopilotState {
1117 excerpt_id: Option<ExcerptId>,
1118 pending_refresh: Task<Option<()>>,
1119 pending_cycling_refresh: Task<Option<()>>,
1120 cycled: bool,
1121 completions: Vec<copilot::Completion>,
1122 active_completion_index: usize,
1123 suggestion: Option<Inlay>,
1124}
1125
1126impl Default for CopilotState {
1127 fn default() -> Self {
1128 Self {
1129 excerpt_id: None,
1130 pending_cycling_refresh: Task::ready(Some(())),
1131 pending_refresh: Task::ready(Some(())),
1132 completions: Default::default(),
1133 active_completion_index: 0,
1134 cycled: false,
1135 suggestion: None,
1136 }
1137 }
1138}
1139
1140impl CopilotState {
1141 fn active_completion(&self) -> Option<&copilot::Completion> {
1142 self.completions.get(self.active_completion_index)
1143 }
1144
1145 fn text_for_active_completion(
1146 &self,
1147 cursor: Anchor,
1148 buffer: &MultiBufferSnapshot,
1149 ) -> Option<&str> {
1150 use language::ToOffset as _;
1151
1152 let completion = self.active_completion()?;
1153 let excerpt_id = self.excerpt_id?;
1154 let completion_buffer = buffer.buffer_for_excerpt(excerpt_id)?;
1155 if excerpt_id != cursor.excerpt_id
1156 || !completion.range.start.is_valid(completion_buffer)
1157 || !completion.range.end.is_valid(completion_buffer)
1158 {
1159 return None;
1160 }
1161
1162 let mut completion_range = completion.range.to_offset(&completion_buffer);
1163 let prefix_len = Self::common_prefix(
1164 completion_buffer.chars_for_range(completion_range.clone()),
1165 completion.text.chars(),
1166 );
1167 completion_range.start += prefix_len;
1168 let suffix_len = Self::common_prefix(
1169 completion_buffer.reversed_chars_for_range(completion_range.clone()),
1170 completion.text[prefix_len..].chars().rev(),
1171 );
1172 completion_range.end = completion_range.end.saturating_sub(suffix_len);
1173
1174 if completion_range.is_empty()
1175 && completion_range.start == cursor.text_anchor.to_offset(&completion_buffer)
1176 {
1177 Some(&completion.text[prefix_len..completion.text.len() - suffix_len])
1178 } else {
1179 None
1180 }
1181 }
1182
1183 fn cycle_completions(&mut self, direction: Direction) {
1184 match direction {
1185 Direction::Prev => {
1186 self.active_completion_index = if self.active_completion_index == 0 {
1187 self.completions.len().saturating_sub(1)
1188 } else {
1189 self.active_completion_index - 1
1190 };
1191 }
1192 Direction::Next => {
1193 if self.completions.len() == 0 {
1194 self.active_completion_index = 0
1195 } else {
1196 self.active_completion_index =
1197 (self.active_completion_index + 1) % self.completions.len();
1198 }
1199 }
1200 }
1201 }
1202
1203 fn push_completion(&mut self, new_completion: copilot::Completion) {
1204 for completion in &self.completions {
1205 if completion.text == new_completion.text && completion.range == new_completion.range {
1206 return;
1207 }
1208 }
1209 self.completions.push(new_completion);
1210 }
1211
1212 fn common_prefix<T1: Iterator<Item = char>, T2: Iterator<Item = char>>(a: T1, b: T2) -> usize {
1213 a.zip(b)
1214 .take_while(|(a, b)| a == b)
1215 .map(|(a, _)| a.len_utf8())
1216 .sum()
1217 }
1218}
1219
1220#[derive(Debug)]
1221struct ActiveDiagnosticGroup {
1222 primary_range: Range<Anchor>,
1223 primary_message: String,
1224 blocks: HashMap<BlockId, Diagnostic>,
1225 is_valid: bool,
1226}
1227
1228#[derive(Serialize, Deserialize)]
1229pub struct ClipboardSelection {
1230 pub len: usize,
1231 pub is_entire_line: bool,
1232 pub first_line_indent: u32,
1233}
1234
1235#[derive(Debug)]
1236pub(crate) struct NavigationData {
1237 cursor_anchor: Anchor,
1238 cursor_position: Point,
1239 scroll_anchor: ScrollAnchor,
1240 scroll_top_row: u32,
1241}
1242
1243enum GotoDefinitionKind {
1244 Symbol,
1245 Type,
1246}
1247
1248#[derive(Debug, Clone)]
1249enum InlayHintRefreshReason {
1250 Toggle(bool),
1251 SettingsChange(InlayHintSettings),
1252 NewLinesShown,
1253 BufferEdited(HashSet<Arc<Language>>),
1254 RefreshRequested,
1255 ExcerptsRemoved(Vec<ExcerptId>),
1256}
1257impl InlayHintRefreshReason {
1258 fn description(&self) -> &'static str {
1259 match self {
1260 Self::Toggle(_) => "toggle",
1261 Self::SettingsChange(_) => "settings change",
1262 Self::NewLinesShown => "new lines shown",
1263 Self::BufferEdited(_) => "buffer edited",
1264 Self::RefreshRequested => "refresh requested",
1265 Self::ExcerptsRemoved(_) => "excerpts removed",
1266 }
1267 }
1268}
1269
1270impl Editor {
1271 pub fn single_line(cx: &mut ViewContext<Self>) -> Self {
1272 let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), String::new()));
1273 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1274 Self::new(EditorMode::SingleLine, buffer, None, cx)
1275 }
1276
1277 pub fn multi_line(cx: &mut ViewContext<Self>) -> Self {
1278 let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), String::new()));
1279 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1280 Self::new(EditorMode::Full, buffer, None, cx)
1281 }
1282
1283 pub fn auto_height(max_lines: usize, cx: &mut ViewContext<Self>) -> Self {
1284 let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), String::new()));
1285 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1286 Self::new(EditorMode::AutoHeight { max_lines }, buffer, None, cx)
1287 }
1288
1289 pub fn for_buffer(
1290 buffer: Model<Buffer>,
1291 project: Option<Model<Project>>,
1292 cx: &mut ViewContext<Self>,
1293 ) -> Self {
1294 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1295 Self::new(EditorMode::Full, buffer, project, cx)
1296 }
1297
1298 pub fn for_multibuffer(
1299 buffer: Model<MultiBuffer>,
1300 project: Option<Model<Project>>,
1301 cx: &mut ViewContext<Self>,
1302 ) -> Self {
1303 Self::new(EditorMode::Full, buffer, project, cx)
1304 }
1305
1306 pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
1307 let mut clone = Self::new(self.mode, self.buffer.clone(), self.project.clone(), cx);
1308 self.display_map.update(cx, |display_map, cx| {
1309 let snapshot = display_map.snapshot(cx);
1310 clone.display_map.update(cx, |display_map, cx| {
1311 display_map.set_state(&snapshot, cx);
1312 });
1313 });
1314 clone.selections.clone_state(&self.selections);
1315 clone.scroll_manager.clone_state(&self.scroll_manager);
1316 clone.searchable = self.searchable;
1317 clone
1318 }
1319
1320 fn new(
1321 mode: EditorMode,
1322 buffer: Model<MultiBuffer>,
1323 project: Option<Model<Project>>,
1324 cx: &mut ViewContext<Self>,
1325 ) -> Self {
1326 let style = cx.text_style();
1327 let font_size = style.font_size.to_pixels(cx.rem_size());
1328 let display_map = cx.new_model(|cx| {
1329 DisplayMap::new(buffer.clone(), style.font(), font_size, None, 2, 1, cx)
1330 });
1331
1332 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1333
1334 let blink_manager = cx.new_model(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1335
1336 let soft_wrap_mode_override =
1337 (mode == EditorMode::SingleLine).then(|| language_settings::SoftWrap::None);
1338
1339 let mut project_subscriptions = Vec::new();
1340 if mode == EditorMode::Full {
1341 if let Some(project) = project.as_ref() {
1342 if buffer.read(cx).is_singleton() {
1343 project_subscriptions.push(cx.observe(project, |_, _, cx| {
1344 cx.emit(EditorEvent::TitleChanged);
1345 }));
1346 }
1347 project_subscriptions.push(cx.subscribe(project, |editor, _, event, cx| {
1348 if let project::Event::RefreshInlayHints = event {
1349 editor.refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1350 };
1351 }));
1352 }
1353 }
1354
1355 let inlay_hint_settings = inlay_hint_settings(
1356 selections.newest_anchor().head(),
1357 &buffer.read(cx).snapshot(cx),
1358 cx,
1359 );
1360
1361 let focus_handle = cx.focus_handle();
1362 cx.on_focus(&focus_handle, Self::handle_focus).detach();
1363 cx.on_blur(&focus_handle, Self::handle_blur).detach();
1364
1365 let mut this = Self {
1366 handle: cx.view().downgrade(),
1367 focus_handle,
1368 buffer: buffer.clone(),
1369 display_map: display_map.clone(),
1370 selections,
1371 scroll_manager: ScrollManager::new(),
1372 columnar_selection_tail: None,
1373 add_selections_state: None,
1374 select_next_state: None,
1375 select_prev_state: None,
1376 selection_history: Default::default(),
1377 autoclose_regions: Default::default(),
1378 snippet_stack: Default::default(),
1379 select_larger_syntax_node_stack: Vec::new(),
1380 ime_transaction: Default::default(),
1381 active_diagnostics: None,
1382 soft_wrap_mode_override,
1383 completion_provider: project.clone().map(|project| Box::new(project) as _),
1384 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1385 project,
1386 blink_manager: blink_manager.clone(),
1387 show_local_selections: true,
1388 mode,
1389 show_gutter: mode == EditorMode::Full,
1390 show_wrap_guides: None,
1391 placeholder_text: None,
1392 highlighted_rows: None,
1393 background_highlights: Default::default(),
1394 inlay_background_highlights: Default::default(),
1395 nav_history: None,
1396 context_menu: RwLock::new(None),
1397 mouse_context_menu: None,
1398 completion_tasks: Default::default(),
1399 next_completion_id: 0,
1400 next_inlay_id: 0,
1401 available_code_actions: Default::default(),
1402 code_actions_task: Default::default(),
1403 document_highlights_task: Default::default(),
1404 pending_rename: Default::default(),
1405 searchable: true,
1406 cursor_shape: Default::default(),
1407 autoindent_mode: Some(AutoindentMode::EachLine),
1408 collapse_matches: false,
1409 workspace: None,
1410 keymap_context_layers: Default::default(),
1411 input_enabled: true,
1412 read_only: false,
1413 leader_peer_id: None,
1414 remote_id: None,
1415 hover_state: Default::default(),
1416 link_go_to_definition_state: Default::default(),
1417 copilot_state: Default::default(),
1418 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1419 gutter_hovered: false,
1420 pixel_position_of_newest_cursor: None,
1421 gutter_width: Default::default(),
1422 style: None,
1423 show_cursor_names: false,
1424 hovered_cursor: Default::default(),
1425 editor_actions: Default::default(),
1426 show_copilot_suggestions: mode == EditorMode::Full,
1427 _subscriptions: vec![
1428 cx.observe(&buffer, Self::on_buffer_changed),
1429 cx.subscribe(&buffer, Self::on_buffer_event),
1430 cx.observe(&display_map, Self::on_display_map_changed),
1431 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1432 cx.observe_global::<SettingsStore>(Self::settings_changed),
1433 observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
1434 cx.observe_window_activation(|editor, cx| {
1435 let active = cx.is_window_active();
1436 editor.blink_manager.update(cx, |blink_manager, cx| {
1437 if active {
1438 blink_manager.enable(cx);
1439 } else {
1440 blink_manager.show_cursor(cx);
1441 blink_manager.disable(cx);
1442 }
1443 });
1444 }),
1445 ],
1446 };
1447
1448 this._subscriptions.extend(project_subscriptions);
1449
1450 this.end_selection(cx);
1451 this.scroll_manager.show_scrollbar(cx);
1452
1453 if mode == EditorMode::Full {
1454 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
1455 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1456 }
1457
1458 this.report_editor_event("open", None, cx);
1459 this
1460 }
1461
1462 fn key_context(&self, cx: &AppContext) -> KeyContext {
1463 let mut key_context = KeyContext::default();
1464 key_context.add("Editor");
1465 let mode = match self.mode {
1466 EditorMode::SingleLine => "single_line",
1467 EditorMode::AutoHeight { .. } => "auto_height",
1468 EditorMode::Full => "full",
1469 };
1470 key_context.set("mode", mode);
1471 if self.pending_rename.is_some() {
1472 key_context.add("renaming");
1473 }
1474 if self.context_menu_visible() {
1475 match self.context_menu.read().as_ref() {
1476 Some(ContextMenu::Completions(_)) => {
1477 key_context.add("menu");
1478 key_context.add("showing_completions")
1479 }
1480 Some(ContextMenu::CodeActions(_)) => {
1481 key_context.add("menu");
1482 key_context.add("showing_code_actions")
1483 }
1484 None => {}
1485 }
1486 }
1487
1488 for layer in self.keymap_context_layers.values() {
1489 key_context.extend(layer);
1490 }
1491
1492 if let Some(extension) = self
1493 .buffer
1494 .read(cx)
1495 .as_singleton()
1496 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
1497 {
1498 key_context.set("extension", extension.to_string());
1499 }
1500
1501 key_context
1502 }
1503
1504 pub fn new_file(
1505 workspace: &mut Workspace,
1506 _: &workspace::NewFile,
1507 cx: &mut ViewContext<Workspace>,
1508 ) {
1509 let project = workspace.project().clone();
1510 if project.read(cx).is_remote() {
1511 cx.propagate();
1512 } else if let Some(buffer) = project
1513 .update(cx, |project, cx| project.create_buffer("", None, cx))
1514 .log_err()
1515 {
1516 workspace.add_item(
1517 Box::new(cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx))),
1518 cx,
1519 );
1520 }
1521 }
1522
1523 pub fn new_file_in_direction(
1524 workspace: &mut Workspace,
1525 action: &workspace::NewFileInDirection,
1526 cx: &mut ViewContext<Workspace>,
1527 ) {
1528 let project = workspace.project().clone();
1529 if project.read(cx).is_remote() {
1530 cx.propagate();
1531 } else if let Some(buffer) = project
1532 .update(cx, |project, cx| project.create_buffer("", None, cx))
1533 .log_err()
1534 {
1535 workspace.split_item(
1536 action.0,
1537 Box::new(cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx))),
1538 cx,
1539 );
1540 }
1541 }
1542
1543 pub fn replica_id(&self, cx: &AppContext) -> ReplicaId {
1544 self.buffer.read(cx).replica_id()
1545 }
1546
1547 pub fn leader_peer_id(&self) -> Option<PeerId> {
1548 self.leader_peer_id
1549 }
1550
1551 pub fn buffer(&self) -> &Model<MultiBuffer> {
1552 &self.buffer
1553 }
1554
1555 pub fn workspace(&self) -> Option<View<Workspace>> {
1556 self.workspace.as_ref()?.0.upgrade()
1557 }
1558
1559 pub fn pane(&self, cx: &AppContext) -> Option<View<Pane>> {
1560 self.workspace()?.read(cx).pane_for(&self.handle.upgrade()?)
1561 }
1562
1563 pub fn title<'a>(&self, cx: &'a AppContext) -> Cow<'a, str> {
1564 self.buffer().read(cx).title(cx)
1565 }
1566
1567 pub fn snapshot(&mut self, cx: &mut WindowContext) -> EditorSnapshot {
1568 EditorSnapshot {
1569 mode: self.mode,
1570 show_gutter: self.show_gutter,
1571 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1572 scroll_anchor: self.scroll_manager.anchor(),
1573 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
1574 placeholder_text: self.placeholder_text.clone(),
1575 is_focused: self.focus_handle.is_focused(cx),
1576 }
1577 }
1578
1579 pub fn language_at<'a, T: ToOffset>(
1580 &self,
1581 point: T,
1582 cx: &'a AppContext,
1583 ) -> Option<Arc<Language>> {
1584 self.buffer.read(cx).language_at(point, cx)
1585 }
1586
1587 pub fn file_at<'a, T: ToOffset>(
1588 &self,
1589 point: T,
1590 cx: &'a AppContext,
1591 ) -> Option<Arc<dyn language::File>> {
1592 self.buffer.read(cx).read(cx).file_at(point).cloned()
1593 }
1594
1595 pub fn active_excerpt(
1596 &self,
1597 cx: &AppContext,
1598 ) -> Option<(ExcerptId, Model<Buffer>, Range<text::Anchor>)> {
1599 self.buffer
1600 .read(cx)
1601 .excerpt_containing(self.selections.newest_anchor().head(), cx)
1602 }
1603
1604 pub fn mode(&self) -> EditorMode {
1605 self.mode
1606 }
1607
1608 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
1609 self.collaboration_hub.as_deref()
1610 }
1611
1612 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
1613 self.collaboration_hub = Some(hub);
1614 }
1615
1616 pub fn set_completion_provider(&mut self, hub: Box<dyn CompletionProvider>) {
1617 self.completion_provider = Some(hub);
1618 }
1619
1620 pub fn placeholder_text(&self) -> Option<&str> {
1621 self.placeholder_text.as_deref()
1622 }
1623
1624 pub fn set_placeholder_text(
1625 &mut self,
1626 placeholder_text: impl Into<Arc<str>>,
1627 cx: &mut ViewContext<Self>,
1628 ) {
1629 let placeholder_text = Some(placeholder_text.into());
1630 if self.placeholder_text != placeholder_text {
1631 self.placeholder_text = placeholder_text;
1632 cx.notify();
1633 }
1634 }
1635
1636 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
1637 self.cursor_shape = cursor_shape;
1638 cx.notify();
1639 }
1640
1641 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
1642 self.collapse_matches = collapse_matches;
1643 }
1644
1645 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
1646 if self.collapse_matches {
1647 return range.start..range.start;
1648 }
1649 range.clone()
1650 }
1651
1652 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut ViewContext<Self>) {
1653 if self.display_map.read(cx).clip_at_line_ends != clip {
1654 self.display_map
1655 .update(cx, |map, _| map.clip_at_line_ends = clip);
1656 }
1657 }
1658
1659 pub fn set_keymap_context_layer<Tag: 'static>(
1660 &mut self,
1661 context: KeyContext,
1662 cx: &mut ViewContext<Self>,
1663 ) {
1664 self.keymap_context_layers
1665 .insert(TypeId::of::<Tag>(), context);
1666 cx.notify();
1667 }
1668
1669 pub fn remove_keymap_context_layer<Tag: 'static>(&mut self, cx: &mut ViewContext<Self>) {
1670 self.keymap_context_layers.remove(&TypeId::of::<Tag>());
1671 cx.notify();
1672 }
1673
1674 pub fn set_input_enabled(&mut self, input_enabled: bool) {
1675 self.input_enabled = input_enabled;
1676 }
1677
1678 pub fn set_autoindent(&mut self, autoindent: bool) {
1679 if autoindent {
1680 self.autoindent_mode = Some(AutoindentMode::EachLine);
1681 } else {
1682 self.autoindent_mode = None;
1683 }
1684 }
1685
1686 pub fn read_only(&self, cx: &AppContext) -> bool {
1687 self.read_only || self.buffer.read(cx).read_only()
1688 }
1689
1690 pub fn set_read_only(&mut self, read_only: bool) {
1691 self.read_only = read_only;
1692 }
1693
1694 pub fn set_show_copilot_suggestions(&mut self, show_copilot_suggestions: bool) {
1695 self.show_copilot_suggestions = show_copilot_suggestions;
1696 }
1697
1698 fn selections_did_change(
1699 &mut self,
1700 local: bool,
1701 old_cursor_position: &Anchor,
1702 cx: &mut ViewContext<Self>,
1703 ) {
1704 if self.focus_handle.is_focused(cx) && self.leader_peer_id.is_none() {
1705 self.buffer.update(cx, |buffer, cx| {
1706 buffer.set_active_selections(
1707 &self.selections.disjoint_anchors(),
1708 self.selections.line_mode,
1709 self.cursor_shape,
1710 cx,
1711 )
1712 });
1713 }
1714
1715 let display_map = self
1716 .display_map
1717 .update(cx, |display_map, cx| display_map.snapshot(cx));
1718 let buffer = &display_map.buffer_snapshot;
1719 self.add_selections_state = None;
1720 self.select_next_state = None;
1721 self.select_prev_state = None;
1722 self.select_larger_syntax_node_stack.clear();
1723 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
1724 self.snippet_stack
1725 .invalidate(&self.selections.disjoint_anchors(), buffer);
1726 self.take_rename(false, cx);
1727
1728 let new_cursor_position = self.selections.newest_anchor().head();
1729
1730 self.push_to_nav_history(
1731 old_cursor_position.clone(),
1732 Some(new_cursor_position.to_point(buffer)),
1733 cx,
1734 );
1735
1736 if local {
1737 let new_cursor_position = self.selections.newest_anchor().head();
1738 let mut context_menu = self.context_menu.write();
1739 let completion_menu = match context_menu.as_ref() {
1740 Some(ContextMenu::Completions(menu)) => Some(menu),
1741
1742 _ => {
1743 *context_menu = None;
1744 None
1745 }
1746 };
1747
1748 if let Some(completion_menu) = completion_menu {
1749 let cursor_position = new_cursor_position.to_offset(buffer);
1750 let (word_range, kind) =
1751 buffer.surrounding_word(completion_menu.initial_position.clone());
1752 if kind == Some(CharKind::Word)
1753 && word_range.to_inclusive().contains(&cursor_position)
1754 {
1755 let mut completion_menu = completion_menu.clone();
1756 drop(context_menu);
1757
1758 let query = Self::completion_query(buffer, cursor_position);
1759 cx.spawn(move |this, mut cx| async move {
1760 completion_menu
1761 .filter(query.as_deref(), cx.background_executor().clone())
1762 .await;
1763
1764 this.update(&mut cx, |this, cx| {
1765 let mut context_menu = this.context_menu.write();
1766 let Some(ContextMenu::Completions(menu)) = context_menu.as_ref() else {
1767 return;
1768 };
1769
1770 if menu.id > completion_menu.id {
1771 return;
1772 }
1773
1774 *context_menu = Some(ContextMenu::Completions(completion_menu));
1775 drop(context_menu);
1776 cx.notify();
1777 })
1778 })
1779 .detach();
1780
1781 self.show_completions(&ShowCompletions, cx);
1782 } else {
1783 drop(context_menu);
1784 self.hide_context_menu(cx);
1785 }
1786 } else {
1787 drop(context_menu);
1788 }
1789
1790 hide_hover(self, cx);
1791
1792 if old_cursor_position.to_display_point(&display_map).row()
1793 != new_cursor_position.to_display_point(&display_map).row()
1794 {
1795 self.available_code_actions.take();
1796 }
1797 self.refresh_code_actions(cx);
1798 self.refresh_document_highlights(cx);
1799 refresh_matching_bracket_highlights(self, cx);
1800 self.discard_copilot_suggestion(cx);
1801 }
1802
1803 self.blink_manager.update(cx, BlinkManager::pause_blinking);
1804 cx.emit(EditorEvent::SelectionsChanged { local });
1805
1806 if self.selections.disjoint_anchors().len() == 1 {
1807 cx.emit(SearchEvent::ActiveMatchChanged)
1808 }
1809
1810 cx.notify();
1811 }
1812
1813 pub fn change_selections<R>(
1814 &mut self,
1815 autoscroll: Option<Autoscroll>,
1816 cx: &mut ViewContext<Self>,
1817 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
1818 ) -> R {
1819 let old_cursor_position = self.selections.newest_anchor().head();
1820 self.push_to_selection_history();
1821
1822 let (changed, result) = self.selections.change_with(cx, change);
1823
1824 if changed {
1825 if let Some(autoscroll) = autoscroll {
1826 self.request_autoscroll(autoscroll, cx);
1827 }
1828 self.selections_did_change(true, &old_cursor_position, cx);
1829 }
1830
1831 result
1832 }
1833
1834 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
1835 where
1836 I: IntoIterator<Item = (Range<S>, T)>,
1837 S: ToOffset,
1838 T: Into<Arc<str>>,
1839 {
1840 if self.read_only(cx) {
1841 return;
1842 }
1843
1844 self.buffer
1845 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
1846 }
1847
1848 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
1849 where
1850 I: IntoIterator<Item = (Range<S>, T)>,
1851 S: ToOffset,
1852 T: Into<Arc<str>>,
1853 {
1854 if self.read_only(cx) {
1855 return;
1856 }
1857
1858 self.buffer.update(cx, |buffer, cx| {
1859 buffer.edit(edits, self.autoindent_mode.clone(), cx)
1860 });
1861 }
1862
1863 pub fn edit_with_block_indent<I, S, T>(
1864 &mut self,
1865 edits: I,
1866 original_indent_columns: Vec<u32>,
1867 cx: &mut ViewContext<Self>,
1868 ) where
1869 I: IntoIterator<Item = (Range<S>, T)>,
1870 S: ToOffset,
1871 T: Into<Arc<str>>,
1872 {
1873 if self.read_only(cx) {
1874 return;
1875 }
1876
1877 self.buffer.update(cx, |buffer, cx| {
1878 buffer.edit(
1879 edits,
1880 Some(AutoindentMode::Block {
1881 original_indent_columns,
1882 }),
1883 cx,
1884 )
1885 });
1886 }
1887
1888 fn select(&mut self, phase: SelectPhase, cx: &mut ViewContext<Self>) {
1889 self.hide_context_menu(cx);
1890
1891 match phase {
1892 SelectPhase::Begin {
1893 position,
1894 add,
1895 click_count,
1896 } => self.begin_selection(position, add, click_count, cx),
1897 SelectPhase::BeginColumnar {
1898 position,
1899 goal_column,
1900 } => self.begin_columnar_selection(position, goal_column, cx),
1901 SelectPhase::Extend {
1902 position,
1903 click_count,
1904 } => self.extend_selection(position, click_count, cx),
1905 SelectPhase::Update {
1906 position,
1907 goal_column,
1908 scroll_position,
1909 } => self.update_selection(position, goal_column, scroll_position, cx),
1910 SelectPhase::End => self.end_selection(cx),
1911 }
1912 }
1913
1914 fn extend_selection(
1915 &mut self,
1916 position: DisplayPoint,
1917 click_count: usize,
1918 cx: &mut ViewContext<Self>,
1919 ) {
1920 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1921 let tail = self.selections.newest::<usize>(cx).tail();
1922 self.begin_selection(position, false, click_count, cx);
1923
1924 let position = position.to_offset(&display_map, Bias::Left);
1925 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
1926
1927 let mut pending_selection = self
1928 .selections
1929 .pending_anchor()
1930 .expect("extend_selection not called with pending selection");
1931 if position >= tail {
1932 pending_selection.start = tail_anchor;
1933 } else {
1934 pending_selection.end = tail_anchor;
1935 pending_selection.reversed = true;
1936 }
1937
1938 let mut pending_mode = self.selections.pending_mode().unwrap();
1939 match &mut pending_mode {
1940 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
1941 _ => {}
1942 }
1943
1944 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
1945 s.set_pending(pending_selection, pending_mode)
1946 });
1947 }
1948
1949 fn begin_selection(
1950 &mut self,
1951 position: DisplayPoint,
1952 add: bool,
1953 click_count: usize,
1954 cx: &mut ViewContext<Self>,
1955 ) {
1956 if !self.focus_handle.is_focused(cx) {
1957 cx.focus(&self.focus_handle);
1958 }
1959
1960 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1961 let buffer = &display_map.buffer_snapshot;
1962 let newest_selection = self.selections.newest_anchor().clone();
1963 let position = display_map.clip_point(position, Bias::Left);
1964
1965 let start;
1966 let end;
1967 let mode;
1968 let auto_scroll;
1969 match click_count {
1970 1 => {
1971 start = buffer.anchor_before(position.to_point(&display_map));
1972 end = start.clone();
1973 mode = SelectMode::Character;
1974 auto_scroll = true;
1975 }
1976 2 => {
1977 let range = movement::surrounding_word(&display_map, position);
1978 start = buffer.anchor_before(range.start.to_point(&display_map));
1979 end = buffer.anchor_before(range.end.to_point(&display_map));
1980 mode = SelectMode::Word(start.clone()..end.clone());
1981 auto_scroll = true;
1982 }
1983 3 => {
1984 let position = display_map
1985 .clip_point(position, Bias::Left)
1986 .to_point(&display_map);
1987 let line_start = display_map.prev_line_boundary(position).0;
1988 let next_line_start = buffer.clip_point(
1989 display_map.next_line_boundary(position).0 + Point::new(1, 0),
1990 Bias::Left,
1991 );
1992 start = buffer.anchor_before(line_start);
1993 end = buffer.anchor_before(next_line_start);
1994 mode = SelectMode::Line(start.clone()..end.clone());
1995 auto_scroll = true;
1996 }
1997 _ => {
1998 start = buffer.anchor_before(0);
1999 end = buffer.anchor_before(buffer.len());
2000 mode = SelectMode::All;
2001 auto_scroll = false;
2002 }
2003 }
2004
2005 self.change_selections(auto_scroll.then(|| Autoscroll::newest()), cx, |s| {
2006 if !add {
2007 s.clear_disjoint();
2008 } else if click_count > 1 {
2009 s.delete(newest_selection.id)
2010 }
2011
2012 s.set_pending_anchor_range(start..end, mode);
2013 });
2014 }
2015
2016 fn begin_columnar_selection(
2017 &mut self,
2018 position: DisplayPoint,
2019 goal_column: u32,
2020 cx: &mut ViewContext<Self>,
2021 ) {
2022 if !self.focus_handle.is_focused(cx) {
2023 cx.focus(&self.focus_handle);
2024 }
2025
2026 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2027 let tail = self.selections.newest::<Point>(cx).tail();
2028 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2029
2030 self.select_columns(
2031 tail.to_display_point(&display_map),
2032 position,
2033 goal_column,
2034 &display_map,
2035 cx,
2036 );
2037 }
2038
2039 fn update_selection(
2040 &mut self,
2041 position: DisplayPoint,
2042 goal_column: u32,
2043 scroll_position: gpui::Point<f32>,
2044 cx: &mut ViewContext<Self>,
2045 ) {
2046 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2047
2048 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2049 let tail = tail.to_display_point(&display_map);
2050 self.select_columns(tail, position, goal_column, &display_map, cx);
2051 } else if let Some(mut pending) = self.selections.pending_anchor() {
2052 let buffer = self.buffer.read(cx).snapshot(cx);
2053 let head;
2054 let tail;
2055 let mode = self.selections.pending_mode().unwrap();
2056 match &mode {
2057 SelectMode::Character => {
2058 head = position.to_point(&display_map);
2059 tail = pending.tail().to_point(&buffer);
2060 }
2061 SelectMode::Word(original_range) => {
2062 let original_display_range = original_range.start.to_display_point(&display_map)
2063 ..original_range.end.to_display_point(&display_map);
2064 let original_buffer_range = original_display_range.start.to_point(&display_map)
2065 ..original_display_range.end.to_point(&display_map);
2066 if movement::is_inside_word(&display_map, position)
2067 || original_display_range.contains(&position)
2068 {
2069 let word_range = movement::surrounding_word(&display_map, position);
2070 if word_range.start < original_display_range.start {
2071 head = word_range.start.to_point(&display_map);
2072 } else {
2073 head = word_range.end.to_point(&display_map);
2074 }
2075 } else {
2076 head = position.to_point(&display_map);
2077 }
2078
2079 if head <= original_buffer_range.start {
2080 tail = original_buffer_range.end;
2081 } else {
2082 tail = original_buffer_range.start;
2083 }
2084 }
2085 SelectMode::Line(original_range) => {
2086 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2087
2088 let position = display_map
2089 .clip_point(position, Bias::Left)
2090 .to_point(&display_map);
2091 let line_start = display_map.prev_line_boundary(position).0;
2092 let next_line_start = buffer.clip_point(
2093 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2094 Bias::Left,
2095 );
2096
2097 if line_start < original_range.start {
2098 head = line_start
2099 } else {
2100 head = next_line_start
2101 }
2102
2103 if head <= original_range.start {
2104 tail = original_range.end;
2105 } else {
2106 tail = original_range.start;
2107 }
2108 }
2109 SelectMode::All => {
2110 return;
2111 }
2112 };
2113
2114 if head < tail {
2115 pending.start = buffer.anchor_before(head);
2116 pending.end = buffer.anchor_before(tail);
2117 pending.reversed = true;
2118 } else {
2119 pending.start = buffer.anchor_before(tail);
2120 pending.end = buffer.anchor_before(head);
2121 pending.reversed = false;
2122 }
2123
2124 self.change_selections(None, cx, |s| {
2125 s.set_pending(pending, mode);
2126 });
2127 } else {
2128 log::error!("update_selection dispatched with no pending selection");
2129 return;
2130 }
2131
2132 self.set_scroll_position(scroll_position, cx);
2133 cx.notify();
2134 }
2135
2136 fn end_selection(&mut self, cx: &mut ViewContext<Self>) {
2137 self.columnar_selection_tail.take();
2138 if self.selections.pending_anchor().is_some() {
2139 let selections = self.selections.all::<usize>(cx);
2140 self.change_selections(None, cx, |s| {
2141 s.select(selections);
2142 s.clear_pending();
2143 });
2144 }
2145 }
2146
2147 fn select_columns(
2148 &mut self,
2149 tail: DisplayPoint,
2150 head: DisplayPoint,
2151 goal_column: u32,
2152 display_map: &DisplaySnapshot,
2153 cx: &mut ViewContext<Self>,
2154 ) {
2155 let start_row = cmp::min(tail.row(), head.row());
2156 let end_row = cmp::max(tail.row(), head.row());
2157 let start_column = cmp::min(tail.column(), goal_column);
2158 let end_column = cmp::max(tail.column(), goal_column);
2159 let reversed = start_column < tail.column();
2160
2161 let selection_ranges = (start_row..=end_row)
2162 .filter_map(|row| {
2163 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2164 let start = display_map
2165 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2166 .to_point(display_map);
2167 let end = display_map
2168 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2169 .to_point(display_map);
2170 if reversed {
2171 Some(end..start)
2172 } else {
2173 Some(start..end)
2174 }
2175 } else {
2176 None
2177 }
2178 })
2179 .collect::<Vec<_>>();
2180
2181 self.change_selections(None, cx, |s| {
2182 s.select_ranges(selection_ranges);
2183 });
2184 cx.notify();
2185 }
2186
2187 pub fn has_pending_nonempty_selection(&self) -> bool {
2188 let pending_nonempty_selection = match self.selections.pending_anchor() {
2189 Some(Selection { start, end, .. }) => start != end,
2190 None => false,
2191 };
2192 pending_nonempty_selection || self.columnar_selection_tail.is_some()
2193 }
2194
2195 pub fn has_pending_selection(&self) -> bool {
2196 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2197 }
2198
2199 pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
2200 if self.take_rename(false, cx).is_some() {
2201 return;
2202 }
2203
2204 if hide_hover(self, cx) {
2205 return;
2206 }
2207
2208 if self.hide_context_menu(cx).is_some() {
2209 return;
2210 }
2211
2212 if self.discard_copilot_suggestion(cx) {
2213 return;
2214 }
2215
2216 if self.snippet_stack.pop().is_some() {
2217 return;
2218 }
2219
2220 if self.mode == EditorMode::Full {
2221 if self.active_diagnostics.is_some() {
2222 self.dismiss_diagnostics(cx);
2223 return;
2224 }
2225
2226 if self.change_selections(Some(Autoscroll::fit()), cx, |s| s.try_cancel()) {
2227 return;
2228 }
2229 }
2230
2231 cx.propagate();
2232 }
2233
2234 pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2235 let text: Arc<str> = text.into();
2236
2237 if self.read_only(cx) {
2238 return;
2239 }
2240
2241 let selections = self.selections.all_adjusted(cx);
2242 let mut brace_inserted = false;
2243 let mut edits = Vec::new();
2244 let mut new_selections = Vec::with_capacity(selections.len());
2245 let mut new_autoclose_regions = Vec::new();
2246 let snapshot = self.buffer.read(cx).read(cx);
2247
2248 for (selection, autoclose_region) in
2249 self.selections_with_autoclose_regions(selections, &snapshot)
2250 {
2251 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
2252 // Determine if the inserted text matches the opening or closing
2253 // bracket of any of this language's bracket pairs.
2254 let mut bracket_pair = None;
2255 let mut is_bracket_pair_start = false;
2256 if !text.is_empty() {
2257 // `text` can be empty when an user is using IME (e.g. Chinese Wubi Simplified)
2258 // and they are removing the character that triggered IME popup.
2259 for (pair, enabled) in scope.brackets() {
2260 if enabled && pair.close && pair.start.ends_with(text.as_ref()) {
2261 bracket_pair = Some(pair.clone());
2262 is_bracket_pair_start = true;
2263 break;
2264 } else if pair.end.as_str() == text.as_ref() {
2265 bracket_pair = Some(pair.clone());
2266 break;
2267 }
2268 }
2269 }
2270
2271 if let Some(bracket_pair) = bracket_pair {
2272 if selection.is_empty() {
2273 if is_bracket_pair_start {
2274 let prefix_len = bracket_pair.start.len() - text.len();
2275
2276 // If the inserted text is a suffix of an opening bracket and the
2277 // selection is preceded by the rest of the opening bracket, then
2278 // insert the closing bracket.
2279 let following_text_allows_autoclose = snapshot
2280 .chars_at(selection.start)
2281 .next()
2282 .map_or(true, |c| scope.should_autoclose_before(c));
2283 let preceding_text_matches_prefix = prefix_len == 0
2284 || (selection.start.column >= (prefix_len as u32)
2285 && snapshot.contains_str_at(
2286 Point::new(
2287 selection.start.row,
2288 selection.start.column - (prefix_len as u32),
2289 ),
2290 &bracket_pair.start[..prefix_len],
2291 ));
2292 if following_text_allows_autoclose && preceding_text_matches_prefix {
2293 let anchor = snapshot.anchor_before(selection.end);
2294 new_selections.push((selection.map(|_| anchor), text.len()));
2295 new_autoclose_regions.push((
2296 anchor,
2297 text.len(),
2298 selection.id,
2299 bracket_pair.clone(),
2300 ));
2301 edits.push((
2302 selection.range(),
2303 format!("{}{}", text, bracket_pair.end).into(),
2304 ));
2305 brace_inserted = true;
2306 continue;
2307 }
2308 }
2309
2310 if let Some(region) = autoclose_region {
2311 // If the selection is followed by an auto-inserted closing bracket,
2312 // then don't insert that closing bracket again; just move the selection
2313 // past the closing bracket.
2314 let should_skip = selection.end == region.range.end.to_point(&snapshot)
2315 && text.as_ref() == region.pair.end.as_str();
2316 if should_skip {
2317 let anchor = snapshot.anchor_after(selection.end);
2318 new_selections
2319 .push((selection.map(|_| anchor), region.pair.end.len()));
2320 continue;
2321 }
2322 }
2323 }
2324 // If an opening bracket is 1 character long and is typed while
2325 // text is selected, then surround that text with the bracket pair.
2326 else if is_bracket_pair_start && bracket_pair.start.chars().count() == 1 {
2327 edits.push((selection.start..selection.start, text.clone()));
2328 edits.push((
2329 selection.end..selection.end,
2330 bracket_pair.end.as_str().into(),
2331 ));
2332 brace_inserted = true;
2333 new_selections.push((
2334 Selection {
2335 id: selection.id,
2336 start: snapshot.anchor_after(selection.start),
2337 end: snapshot.anchor_before(selection.end),
2338 reversed: selection.reversed,
2339 goal: selection.goal,
2340 },
2341 0,
2342 ));
2343 continue;
2344 }
2345 }
2346 }
2347
2348 // If not handling any auto-close operation, then just replace the selected
2349 // text with the given input and move the selection to the end of the
2350 // newly inserted text.
2351 let anchor = snapshot.anchor_after(selection.end);
2352 new_selections.push((selection.map(|_| anchor), 0));
2353 edits.push((selection.start..selection.end, text.clone()));
2354 }
2355
2356 drop(snapshot);
2357 self.transact(cx, |this, cx| {
2358 this.buffer.update(cx, |buffer, cx| {
2359 buffer.edit(edits, this.autoindent_mode.clone(), cx);
2360 });
2361
2362 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
2363 let new_selection_deltas = new_selections.iter().map(|e| e.1);
2364 let snapshot = this.buffer.read(cx).read(cx);
2365 let new_selections = resolve_multiple::<usize, _>(new_anchor_selections, &snapshot)
2366 .zip(new_selection_deltas)
2367 .map(|(selection, delta)| Selection {
2368 id: selection.id,
2369 start: selection.start + delta,
2370 end: selection.end + delta,
2371 reversed: selection.reversed,
2372 goal: SelectionGoal::None,
2373 })
2374 .collect::<Vec<_>>();
2375
2376 let mut i = 0;
2377 for (position, delta, selection_id, pair) in new_autoclose_regions {
2378 let position = position.to_offset(&snapshot) + delta;
2379 let start = snapshot.anchor_before(position);
2380 let end = snapshot.anchor_after(position);
2381 while let Some(existing_state) = this.autoclose_regions.get(i) {
2382 match existing_state.range.start.cmp(&start, &snapshot) {
2383 Ordering::Less => i += 1,
2384 Ordering::Greater => break,
2385 Ordering::Equal => match end.cmp(&existing_state.range.end, &snapshot) {
2386 Ordering::Less => i += 1,
2387 Ordering::Equal => break,
2388 Ordering::Greater => break,
2389 },
2390 }
2391 }
2392 this.autoclose_regions.insert(
2393 i,
2394 AutocloseRegion {
2395 selection_id,
2396 range: start..end,
2397 pair,
2398 },
2399 );
2400 }
2401
2402 drop(snapshot);
2403 let had_active_copilot_suggestion = this.has_active_copilot_suggestion(cx);
2404 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
2405
2406 if !brace_inserted && EditorSettings::get_global(cx).use_on_type_format {
2407 if let Some(on_type_format_task) =
2408 this.trigger_on_type_formatting(text.to_string(), cx)
2409 {
2410 on_type_format_task.detach_and_log_err(cx);
2411 }
2412 }
2413
2414 if had_active_copilot_suggestion {
2415 this.refresh_copilot_suggestions(true, cx);
2416 if !this.has_active_copilot_suggestion(cx) {
2417 this.trigger_completion_on_input(&text, cx);
2418 }
2419 } else {
2420 this.trigger_completion_on_input(&text, cx);
2421 this.refresh_copilot_suggestions(true, cx);
2422 }
2423 });
2424 }
2425
2426 pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
2427 self.transact(cx, |this, cx| {
2428 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
2429 let selections = this.selections.all::<usize>(cx);
2430 let multi_buffer = this.buffer.read(cx);
2431 let buffer = multi_buffer.snapshot(cx);
2432 selections
2433 .iter()
2434 .map(|selection| {
2435 let start_point = selection.start.to_point(&buffer);
2436 let mut indent = buffer.indent_size_for_line(start_point.row);
2437 indent.len = cmp::min(indent.len, start_point.column);
2438 let start = selection.start;
2439 let end = selection.end;
2440 let is_cursor = start == end;
2441 let language_scope = buffer.language_scope_at(start);
2442 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
2443 &language_scope
2444 {
2445 let leading_whitespace_len = buffer
2446 .reversed_chars_at(start)
2447 .take_while(|c| c.is_whitespace() && *c != '\n')
2448 .map(|c| c.len_utf8())
2449 .sum::<usize>();
2450
2451 let trailing_whitespace_len = buffer
2452 .chars_at(end)
2453 .take_while(|c| c.is_whitespace() && *c != '\n')
2454 .map(|c| c.len_utf8())
2455 .sum::<usize>();
2456
2457 let insert_extra_newline =
2458 language.brackets().any(|(pair, enabled)| {
2459 let pair_start = pair.start.trim_end();
2460 let pair_end = pair.end.trim_start();
2461
2462 enabled
2463 && pair.newline
2464 && buffer.contains_str_at(
2465 end + trailing_whitespace_len,
2466 pair_end,
2467 )
2468 && buffer.contains_str_at(
2469 (start - leading_whitespace_len)
2470 .saturating_sub(pair_start.len()),
2471 pair_start,
2472 )
2473 });
2474 // Comment extension on newline is allowed only for cursor selections
2475 let comment_delimiter = language.line_comment_prefix().filter(|_| {
2476 let is_comment_extension_enabled =
2477 multi_buffer.settings_at(0, cx).extend_comment_on_newline;
2478 is_cursor && is_comment_extension_enabled
2479 });
2480 let comment_delimiter = if let Some(delimiter) = comment_delimiter {
2481 buffer
2482 .buffer_line_for_row(start_point.row)
2483 .is_some_and(|(snapshot, range)| {
2484 let mut index_of_first_non_whitespace = 0;
2485 let line_starts_with_comment = snapshot
2486 .chars_for_range(range)
2487 .skip_while(|c| {
2488 let should_skip = c.is_whitespace();
2489 if should_skip {
2490 index_of_first_non_whitespace += 1;
2491 }
2492 should_skip
2493 })
2494 .take(delimiter.len())
2495 .eq(delimiter.chars());
2496 let cursor_is_placed_after_comment_marker =
2497 index_of_first_non_whitespace + delimiter.len()
2498 <= start_point.column as usize;
2499 line_starts_with_comment
2500 && cursor_is_placed_after_comment_marker
2501 })
2502 .then(|| delimiter.clone())
2503 } else {
2504 None
2505 };
2506 (comment_delimiter, insert_extra_newline)
2507 } else {
2508 (None, false)
2509 };
2510
2511 let capacity_for_delimiter = comment_delimiter
2512 .as_deref()
2513 .map(str::len)
2514 .unwrap_or_default();
2515 let mut new_text =
2516 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
2517 new_text.push_str("\n");
2518 new_text.extend(indent.chars());
2519 if let Some(delimiter) = &comment_delimiter {
2520 new_text.push_str(&delimiter);
2521 }
2522 if insert_extra_newline {
2523 new_text = new_text.repeat(2);
2524 }
2525
2526 let anchor = buffer.anchor_after(end);
2527 let new_selection = selection.map(|_| anchor);
2528 (
2529 (start..end, new_text),
2530 (insert_extra_newline, new_selection),
2531 )
2532 })
2533 .unzip()
2534 };
2535
2536 this.edit_with_autoindent(edits, cx);
2537 let buffer = this.buffer.read(cx).snapshot(cx);
2538 let new_selections = selection_fixup_info
2539 .into_iter()
2540 .map(|(extra_newline_inserted, new_selection)| {
2541 let mut cursor = new_selection.end.to_point(&buffer);
2542 if extra_newline_inserted {
2543 cursor.row -= 1;
2544 cursor.column = buffer.line_len(cursor.row);
2545 }
2546 new_selection.map(|_| cursor)
2547 })
2548 .collect();
2549
2550 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
2551 this.refresh_copilot_suggestions(true, cx);
2552 });
2553 }
2554
2555 pub fn newline_above(&mut self, _: &NewlineAbove, cx: &mut ViewContext<Self>) {
2556 let buffer = self.buffer.read(cx);
2557 let snapshot = buffer.snapshot(cx);
2558
2559 let mut edits = Vec::new();
2560 let mut rows = Vec::new();
2561 let mut rows_inserted = 0;
2562
2563 for selection in self.selections.all_adjusted(cx) {
2564 let cursor = selection.head();
2565 let row = cursor.row;
2566
2567 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
2568
2569 let newline = "\n".to_string();
2570 edits.push((start_of_line..start_of_line, newline));
2571
2572 rows.push(row + rows_inserted);
2573 rows_inserted += 1;
2574 }
2575
2576 self.transact(cx, |editor, cx| {
2577 editor.edit(edits, cx);
2578
2579 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
2580 let mut index = 0;
2581 s.move_cursors_with(|map, _, _| {
2582 let row = rows[index];
2583 index += 1;
2584
2585 let point = Point::new(row, 0);
2586 let boundary = map.next_line_boundary(point).1;
2587 let clipped = map.clip_point(boundary, Bias::Left);
2588
2589 (clipped, SelectionGoal::None)
2590 });
2591 });
2592
2593 let mut indent_edits = Vec::new();
2594 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
2595 for row in rows {
2596 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
2597 for (row, indent) in indents {
2598 if indent.len == 0 {
2599 continue;
2600 }
2601
2602 let text = match indent.kind {
2603 IndentKind::Space => " ".repeat(indent.len as usize),
2604 IndentKind::Tab => "\t".repeat(indent.len as usize),
2605 };
2606 let point = Point::new(row, 0);
2607 indent_edits.push((point..point, text));
2608 }
2609 }
2610 editor.edit(indent_edits, cx);
2611 });
2612 }
2613
2614 pub fn newline_below(&mut self, _: &NewlineBelow, cx: &mut ViewContext<Self>) {
2615 let buffer = self.buffer.read(cx);
2616 let snapshot = buffer.snapshot(cx);
2617
2618 let mut edits = Vec::new();
2619 let mut rows = Vec::new();
2620 let mut rows_inserted = 0;
2621
2622 for selection in self.selections.all_adjusted(cx) {
2623 let cursor = selection.head();
2624 let row = cursor.row;
2625
2626 let point = Point::new(row + 1, 0);
2627 let start_of_line = snapshot.clip_point(point, Bias::Left);
2628
2629 let newline = "\n".to_string();
2630 edits.push((start_of_line..start_of_line, newline));
2631
2632 rows_inserted += 1;
2633 rows.push(row + rows_inserted);
2634 }
2635
2636 self.transact(cx, |editor, cx| {
2637 editor.edit(edits, cx);
2638
2639 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
2640 let mut index = 0;
2641 s.move_cursors_with(|map, _, _| {
2642 let row = rows[index];
2643 index += 1;
2644
2645 let point = Point::new(row, 0);
2646 let boundary = map.next_line_boundary(point).1;
2647 let clipped = map.clip_point(boundary, Bias::Left);
2648
2649 (clipped, SelectionGoal::None)
2650 });
2651 });
2652
2653 let mut indent_edits = Vec::new();
2654 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
2655 for row in rows {
2656 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
2657 for (row, indent) in indents {
2658 if indent.len == 0 {
2659 continue;
2660 }
2661
2662 let text = match indent.kind {
2663 IndentKind::Space => " ".repeat(indent.len as usize),
2664 IndentKind::Tab => "\t".repeat(indent.len as usize),
2665 };
2666 let point = Point::new(row, 0);
2667 indent_edits.push((point..point, text));
2668 }
2669 }
2670 editor.edit(indent_edits, cx);
2671 });
2672 }
2673
2674 pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2675 self.insert_with_autoindent_mode(
2676 text,
2677 Some(AutoindentMode::Block {
2678 original_indent_columns: Vec::new(),
2679 }),
2680 cx,
2681 );
2682 }
2683
2684 fn insert_with_autoindent_mode(
2685 &mut self,
2686 text: &str,
2687 autoindent_mode: Option<AutoindentMode>,
2688 cx: &mut ViewContext<Self>,
2689 ) {
2690 if self.read_only(cx) {
2691 return;
2692 }
2693
2694 let text: Arc<str> = text.into();
2695 self.transact(cx, |this, cx| {
2696 let old_selections = this.selections.all_adjusted(cx);
2697 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
2698 let anchors = {
2699 let snapshot = buffer.read(cx);
2700 old_selections
2701 .iter()
2702 .map(|s| {
2703 let anchor = snapshot.anchor_after(s.head());
2704 s.map(|_| anchor)
2705 })
2706 .collect::<Vec<_>>()
2707 };
2708 buffer.edit(
2709 old_selections
2710 .iter()
2711 .map(|s| (s.start..s.end, text.clone())),
2712 autoindent_mode,
2713 cx,
2714 );
2715 anchors
2716 });
2717
2718 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
2719 s.select_anchors(selection_anchors);
2720 })
2721 });
2722 }
2723
2724 fn trigger_completion_on_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2725 if !EditorSettings::get_global(cx).show_completions_on_input {
2726 return;
2727 }
2728
2729 let selection = self.selections.newest_anchor();
2730 if self
2731 .buffer
2732 .read(cx)
2733 .is_completion_trigger(selection.head(), text, cx)
2734 {
2735 self.show_completions(&ShowCompletions, cx);
2736 } else {
2737 self.hide_context_menu(cx);
2738 }
2739 }
2740
2741 /// If any empty selections is touching the start of its innermost containing autoclose
2742 /// region, expand it to select the brackets.
2743 fn select_autoclose_pair(&mut self, cx: &mut ViewContext<Self>) {
2744 let selections = self.selections.all::<usize>(cx);
2745 let buffer = self.buffer.read(cx).read(cx);
2746 let mut new_selections = Vec::new();
2747 for (mut selection, region) in self.selections_with_autoclose_regions(selections, &buffer) {
2748 if let (Some(region), true) = (region, selection.is_empty()) {
2749 let mut range = region.range.to_offset(&buffer);
2750 if selection.start == range.start {
2751 if range.start >= region.pair.start.len() {
2752 range.start -= region.pair.start.len();
2753 if buffer.contains_str_at(range.start, ®ion.pair.start) {
2754 if buffer.contains_str_at(range.end, ®ion.pair.end) {
2755 range.end += region.pair.end.len();
2756 selection.start = range.start;
2757 selection.end = range.end;
2758 }
2759 }
2760 }
2761 }
2762 }
2763 new_selections.push(selection);
2764 }
2765
2766 drop(buffer);
2767 self.change_selections(None, cx, |selections| selections.select(new_selections));
2768 }
2769
2770 /// Iterate the given selections, and for each one, find the smallest surrounding
2771 /// autoclose region. This uses the ordering of the selections and the autoclose
2772 /// regions to avoid repeated comparisons.
2773 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
2774 &'a self,
2775 selections: impl IntoIterator<Item = Selection<D>>,
2776 buffer: &'a MultiBufferSnapshot,
2777 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
2778 let mut i = 0;
2779 let mut regions = self.autoclose_regions.as_slice();
2780 selections.into_iter().map(move |selection| {
2781 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
2782
2783 let mut enclosing = None;
2784 while let Some(pair_state) = regions.get(i) {
2785 if pair_state.range.end.to_offset(buffer) < range.start {
2786 regions = ®ions[i + 1..];
2787 i = 0;
2788 } else if pair_state.range.start.to_offset(buffer) > range.end {
2789 break;
2790 } else {
2791 if pair_state.selection_id == selection.id {
2792 enclosing = Some(pair_state);
2793 }
2794 i += 1;
2795 }
2796 }
2797
2798 (selection.clone(), enclosing)
2799 })
2800 }
2801
2802 /// Remove any autoclose regions that no longer contain their selection.
2803 fn invalidate_autoclose_regions(
2804 &mut self,
2805 mut selections: &[Selection<Anchor>],
2806 buffer: &MultiBufferSnapshot,
2807 ) {
2808 self.autoclose_regions.retain(|state| {
2809 let mut i = 0;
2810 while let Some(selection) = selections.get(i) {
2811 if selection.end.cmp(&state.range.start, buffer).is_lt() {
2812 selections = &selections[1..];
2813 continue;
2814 }
2815 if selection.start.cmp(&state.range.end, buffer).is_gt() {
2816 break;
2817 }
2818 if selection.id == state.selection_id {
2819 return true;
2820 } else {
2821 i += 1;
2822 }
2823 }
2824 false
2825 });
2826 }
2827
2828 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
2829 let offset = position.to_offset(buffer);
2830 let (word_range, kind) = buffer.surrounding_word(offset);
2831 if offset > word_range.start && kind == Some(CharKind::Word) {
2832 Some(
2833 buffer
2834 .text_for_range(word_range.start..offset)
2835 .collect::<String>(),
2836 )
2837 } else {
2838 None
2839 }
2840 }
2841
2842 pub fn toggle_inlay_hints(&mut self, _: &ToggleInlayHints, cx: &mut ViewContext<Self>) {
2843 self.refresh_inlay_hints(
2844 InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled),
2845 cx,
2846 );
2847 }
2848
2849 pub fn inlay_hints_enabled(&self) -> bool {
2850 self.inlay_hint_cache.enabled
2851 }
2852
2853 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut ViewContext<Self>) {
2854 if self.project.is_none() || self.mode != EditorMode::Full {
2855 return;
2856 }
2857
2858 let reason_description = reason.description();
2859 let (invalidate_cache, required_languages) = match reason {
2860 InlayHintRefreshReason::Toggle(enabled) => {
2861 self.inlay_hint_cache.enabled = enabled;
2862 if enabled {
2863 (InvalidationStrategy::RefreshRequested, None)
2864 } else {
2865 self.inlay_hint_cache.clear();
2866 self.splice_inlay_hints(
2867 self.visible_inlay_hints(cx)
2868 .iter()
2869 .map(|inlay| inlay.id)
2870 .collect(),
2871 Vec::new(),
2872 cx,
2873 );
2874 return;
2875 }
2876 }
2877 InlayHintRefreshReason::SettingsChange(new_settings) => {
2878 match self.inlay_hint_cache.update_settings(
2879 &self.buffer,
2880 new_settings,
2881 self.visible_inlay_hints(cx),
2882 cx,
2883 ) {
2884 ControlFlow::Break(Some(InlaySplice {
2885 to_remove,
2886 to_insert,
2887 })) => {
2888 self.splice_inlay_hints(to_remove, to_insert, cx);
2889 return;
2890 }
2891 ControlFlow::Break(None) => return,
2892 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
2893 }
2894 }
2895 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
2896 if let Some(InlaySplice {
2897 to_remove,
2898 to_insert,
2899 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
2900 {
2901 self.splice_inlay_hints(to_remove, to_insert, cx);
2902 }
2903 return;
2904 }
2905 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
2906 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
2907 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
2908 }
2909 InlayHintRefreshReason::RefreshRequested => {
2910 (InvalidationStrategy::RefreshRequested, None)
2911 }
2912 };
2913
2914 if let Some(InlaySplice {
2915 to_remove,
2916 to_insert,
2917 }) = self.inlay_hint_cache.spawn_hint_refresh(
2918 reason_description,
2919 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
2920 invalidate_cache,
2921 cx,
2922 ) {
2923 self.splice_inlay_hints(to_remove, to_insert, cx);
2924 }
2925 }
2926
2927 fn visible_inlay_hints(&self, cx: &ViewContext<'_, Editor>) -> Vec<Inlay> {
2928 self.display_map
2929 .read(cx)
2930 .current_inlays()
2931 .filter(move |inlay| {
2932 Some(inlay.id) != self.copilot_state.suggestion.as_ref().map(|h| h.id)
2933 })
2934 .cloned()
2935 .collect()
2936 }
2937
2938 pub fn excerpts_for_inlay_hints_query(
2939 &self,
2940 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
2941 cx: &mut ViewContext<Editor>,
2942 ) -> HashMap<ExcerptId, (Model<Buffer>, clock::Global, Range<usize>)> {
2943 let Some(project) = self.project.as_ref() else {
2944 return HashMap::default();
2945 };
2946 let project = project.read(cx);
2947 let multi_buffer = self.buffer().read(cx);
2948 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
2949 let multi_buffer_visible_start = self
2950 .scroll_manager
2951 .anchor()
2952 .anchor
2953 .to_point(&multi_buffer_snapshot);
2954 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
2955 multi_buffer_visible_start
2956 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
2957 Bias::Left,
2958 );
2959 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
2960 multi_buffer
2961 .range_to_buffer_ranges(multi_buffer_visible_range, cx)
2962 .into_iter()
2963 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
2964 .filter_map(|(buffer_handle, excerpt_visible_range, excerpt_id)| {
2965 let buffer = buffer_handle.read(cx);
2966 let buffer_file = project::worktree::File::from_dyn(buffer.file())?;
2967 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
2968 let worktree_entry = buffer_worktree
2969 .read(cx)
2970 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
2971 if worktree_entry.is_ignored {
2972 return None;
2973 }
2974
2975 let language = buffer.language()?;
2976 if let Some(restrict_to_languages) = restrict_to_languages {
2977 if !restrict_to_languages.contains(language) {
2978 return None;
2979 }
2980 }
2981 Some((
2982 excerpt_id,
2983 (
2984 buffer_handle,
2985 buffer.version().clone(),
2986 excerpt_visible_range,
2987 ),
2988 ))
2989 })
2990 .collect()
2991 }
2992
2993 pub fn text_layout_details(&self, cx: &WindowContext) -> TextLayoutDetails {
2994 TextLayoutDetails {
2995 text_system: cx.text_system().clone(),
2996 editor_style: self.style.clone().unwrap(),
2997 rem_size: cx.rem_size(),
2998 }
2999 }
3000
3001 fn splice_inlay_hints(
3002 &self,
3003 to_remove: Vec<InlayId>,
3004 to_insert: Vec<Inlay>,
3005 cx: &mut ViewContext<Self>,
3006 ) {
3007 self.display_map.update(cx, |display_map, cx| {
3008 display_map.splice_inlays(to_remove, to_insert, cx);
3009 });
3010 cx.notify();
3011 }
3012
3013 fn trigger_on_type_formatting(
3014 &self,
3015 input: String,
3016 cx: &mut ViewContext<Self>,
3017 ) -> Option<Task<Result<()>>> {
3018 if input.len() != 1 {
3019 return None;
3020 }
3021
3022 let project = self.project.as_ref()?;
3023 let position = self.selections.newest_anchor().head();
3024 let (buffer, buffer_position) = self
3025 .buffer
3026 .read(cx)
3027 .text_anchor_for_position(position.clone(), cx)?;
3028
3029 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
3030 // hence we do LSP request & edit on host side only — add formats to host's history.
3031 let push_to_lsp_host_history = true;
3032 // If this is not the host, append its history with new edits.
3033 let push_to_client_history = project.read(cx).is_remote();
3034
3035 let on_type_formatting = project.update(cx, |project, cx| {
3036 project.on_type_format(
3037 buffer.clone(),
3038 buffer_position,
3039 input,
3040 push_to_lsp_host_history,
3041 cx,
3042 )
3043 });
3044 Some(cx.spawn(|editor, mut cx| async move {
3045 if let Some(transaction) = on_type_formatting.await? {
3046 if push_to_client_history {
3047 buffer
3048 .update(&mut cx, |buffer, _| {
3049 buffer.push_transaction(transaction, Instant::now());
3050 })
3051 .ok();
3052 }
3053 editor.update(&mut cx, |editor, cx| {
3054 editor.refresh_document_highlights(cx);
3055 })?;
3056 }
3057 Ok(())
3058 }))
3059 }
3060
3061 fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) {
3062 if self.pending_rename.is_some() {
3063 return;
3064 }
3065
3066 let Some(provider) = self.completion_provider.as_ref() else {
3067 return;
3068 };
3069
3070 let position = self.selections.newest_anchor().head();
3071 let (buffer, buffer_position) = if let Some(output) = self
3072 .buffer
3073 .read(cx)
3074 .text_anchor_for_position(position.clone(), cx)
3075 {
3076 output
3077 } else {
3078 return;
3079 };
3080
3081 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position.clone());
3082 let completions = provider.completions(&buffer, buffer_position, cx);
3083
3084 let id = post_inc(&mut self.next_completion_id);
3085 let task = cx.spawn(|this, mut cx| {
3086 async move {
3087 let completions = completions.await.log_err();
3088 let (menu, pre_resolve_task) = if let Some(completions) = completions {
3089 let mut menu = CompletionsMenu {
3090 id,
3091 initial_position: position,
3092 match_candidates: completions
3093 .iter()
3094 .enumerate()
3095 .map(|(id, completion)| {
3096 StringMatchCandidate::new(
3097 id,
3098 completion.label.text[completion.label.filter_range.clone()]
3099 .into(),
3100 )
3101 })
3102 .collect(),
3103 buffer,
3104 completions: Arc::new(RwLock::new(completions.into())),
3105 matches: Vec::new().into(),
3106 selected_item: 0,
3107 scroll_handle: UniformListScrollHandle::new(),
3108 };
3109 menu.filter(query.as_deref(), cx.background_executor().clone())
3110 .await;
3111
3112 if menu.matches.is_empty() {
3113 (None, None)
3114 } else {
3115 let pre_resolve_task = this
3116 .update(&mut cx, |editor, cx| {
3117 menu.pre_resolve_completion_documentation(editor, cx)
3118 })
3119 .ok()
3120 .flatten();
3121 (Some(menu), pre_resolve_task)
3122 }
3123 } else {
3124 (None, None)
3125 };
3126
3127 this.update(&mut cx, |this, cx| {
3128 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
3129
3130 let mut context_menu = this.context_menu.write();
3131 match context_menu.as_ref() {
3132 None => {}
3133
3134 Some(ContextMenu::Completions(prev_menu)) => {
3135 if prev_menu.id > id {
3136 return;
3137 }
3138 }
3139
3140 _ => return,
3141 }
3142
3143 if this.focus_handle.is_focused(cx) && menu.is_some() {
3144 let menu = menu.unwrap();
3145 *context_menu = Some(ContextMenu::Completions(menu));
3146 drop(context_menu);
3147 this.discard_copilot_suggestion(cx);
3148 cx.notify();
3149 } else if this.completion_tasks.len() <= 1 {
3150 // If there are no more completion tasks and the last menu was
3151 // empty, we should hide it. If it was already hidden, we should
3152 // also show the copilot suggestion when available.
3153 drop(context_menu);
3154 if this.hide_context_menu(cx).is_none() {
3155 this.update_visible_copilot_suggestion(cx);
3156 }
3157 }
3158 })?;
3159
3160 if let Some(pre_resolve_task) = pre_resolve_task {
3161 pre_resolve_task.await;
3162 }
3163
3164 Ok::<_, anyhow::Error>(())
3165 }
3166 .log_err()
3167 });
3168
3169 self.completion_tasks.push((id, task));
3170 }
3171
3172 pub fn confirm_completion(
3173 &mut self,
3174 action: &ConfirmCompletion,
3175 cx: &mut ViewContext<Self>,
3176 ) -> Option<Task<Result<()>>> {
3177 use language::ToOffset as _;
3178
3179 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
3180 menu
3181 } else {
3182 return None;
3183 };
3184
3185 let mat = completions_menu
3186 .matches
3187 .get(action.item_ix.unwrap_or(completions_menu.selected_item))?;
3188 let buffer_handle = completions_menu.buffer;
3189 let completions = completions_menu.completions.read();
3190 let completion = completions.get(mat.candidate_id)?;
3191 cx.stop_propagation();
3192
3193 let snippet;
3194 let text;
3195 if completion.is_snippet() {
3196 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
3197 text = snippet.as_ref().unwrap().text.clone();
3198 } else {
3199 snippet = None;
3200 text = completion.new_text.clone();
3201 };
3202 let selections = self.selections.all::<usize>(cx);
3203 let buffer = buffer_handle.read(cx);
3204 let old_range = completion.old_range.to_offset(buffer);
3205 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
3206
3207 let newest_selection = self.selections.newest_anchor();
3208 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
3209 return None;
3210 }
3211
3212 let lookbehind = newest_selection
3213 .start
3214 .text_anchor
3215 .to_offset(buffer)
3216 .saturating_sub(old_range.start);
3217 let lookahead = old_range
3218 .end
3219 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
3220 let mut common_prefix_len = old_text
3221 .bytes()
3222 .zip(text.bytes())
3223 .take_while(|(a, b)| a == b)
3224 .count();
3225
3226 let snapshot = self.buffer.read(cx).snapshot(cx);
3227 let mut range_to_replace: Option<Range<isize>> = None;
3228 let mut ranges = Vec::new();
3229 for selection in &selections {
3230 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
3231 let start = selection.start.saturating_sub(lookbehind);
3232 let end = selection.end + lookahead;
3233 if selection.id == newest_selection.id {
3234 range_to_replace = Some(
3235 ((start + common_prefix_len) as isize - selection.start as isize)
3236 ..(end as isize - selection.start as isize),
3237 );
3238 }
3239 ranges.push(start + common_prefix_len..end);
3240 } else {
3241 common_prefix_len = 0;
3242 ranges.clear();
3243 ranges.extend(selections.iter().map(|s| {
3244 if s.id == newest_selection.id {
3245 range_to_replace = Some(
3246 old_range.start.to_offset_utf16(&snapshot).0 as isize
3247 - selection.start as isize
3248 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
3249 - selection.start as isize,
3250 );
3251 old_range.clone()
3252 } else {
3253 s.start..s.end
3254 }
3255 }));
3256 break;
3257 }
3258 }
3259 let text = &text[common_prefix_len..];
3260
3261 cx.emit(EditorEvent::InputHandled {
3262 utf16_range_to_replace: range_to_replace,
3263 text: text.into(),
3264 });
3265
3266 self.transact(cx, |this, cx| {
3267 if let Some(mut snippet) = snippet {
3268 snippet.text = text.to_string();
3269 for tabstop in snippet.tabstops.iter_mut().flatten() {
3270 tabstop.start -= common_prefix_len as isize;
3271 tabstop.end -= common_prefix_len as isize;
3272 }
3273
3274 this.insert_snippet(&ranges, snippet, cx).log_err();
3275 } else {
3276 this.buffer.update(cx, |buffer, cx| {
3277 buffer.edit(
3278 ranges.iter().map(|range| (range.clone(), text)),
3279 this.autoindent_mode.clone(),
3280 cx,
3281 );
3282 });
3283 }
3284
3285 this.refresh_copilot_suggestions(true, cx);
3286 });
3287
3288 let project = self.project.clone()?;
3289 let apply_edits = project.update(cx, |project, cx| {
3290 project.apply_additional_edits_for_completion(
3291 buffer_handle,
3292 completion.clone(),
3293 true,
3294 cx,
3295 )
3296 });
3297 Some(cx.foreground_executor().spawn(async move {
3298 apply_edits.await?;
3299 Ok(())
3300 }))
3301 }
3302
3303 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
3304 let mut context_menu = self.context_menu.write();
3305 if matches!(context_menu.as_ref(), Some(ContextMenu::CodeActions(_))) {
3306 *context_menu = None;
3307 cx.notify();
3308 return;
3309 }
3310 drop(context_menu);
3311
3312 let deployed_from_indicator = action.deployed_from_indicator;
3313 let mut task = self.code_actions_task.take();
3314 cx.spawn(|this, mut cx| async move {
3315 while let Some(prev_task) = task {
3316 prev_task.await;
3317 task = this.update(&mut cx, |this, _| this.code_actions_task.take())?;
3318 }
3319
3320 this.update(&mut cx, |this, cx| {
3321 if this.focus_handle.is_focused(cx) {
3322 if let Some((buffer, actions)) = this.available_code_actions.clone() {
3323 this.completion_tasks.clear();
3324 this.discard_copilot_suggestion(cx);
3325 *this.context_menu.write() =
3326 Some(ContextMenu::CodeActions(CodeActionsMenu {
3327 buffer,
3328 actions,
3329 selected_item: Default::default(),
3330 scroll_handle: UniformListScrollHandle::default(),
3331 deployed_from_indicator,
3332 }));
3333 cx.notify();
3334 }
3335 }
3336 })?;
3337
3338 Ok::<_, anyhow::Error>(())
3339 })
3340 .detach_and_log_err(cx);
3341 }
3342
3343 pub fn confirm_code_action(
3344 &mut self,
3345 action: &ConfirmCodeAction,
3346 cx: &mut ViewContext<Self>,
3347 ) -> Option<Task<Result<()>>> {
3348 let actions_menu = if let ContextMenu::CodeActions(menu) = self.hide_context_menu(cx)? {
3349 menu
3350 } else {
3351 return None;
3352 };
3353 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
3354 let action = actions_menu.actions.get(action_ix)?.clone();
3355 let title = action.lsp_action.title.clone();
3356 let buffer = actions_menu.buffer;
3357 let workspace = self.workspace()?;
3358
3359 let apply_code_actions = workspace
3360 .read(cx)
3361 .project()
3362 .clone()
3363 .update(cx, |project, cx| {
3364 project.apply_code_action(buffer, action, true, cx)
3365 });
3366 let workspace = workspace.downgrade();
3367 Some(cx.spawn(|editor, cx| async move {
3368 let project_transaction = apply_code_actions.await?;
3369 Self::open_project_transaction(&editor, workspace, project_transaction, title, cx).await
3370 }))
3371 }
3372
3373 async fn open_project_transaction(
3374 this: &WeakView<Editor>,
3375 workspace: WeakView<Workspace>,
3376 transaction: ProjectTransaction,
3377 title: String,
3378 mut cx: AsyncWindowContext,
3379 ) -> Result<()> {
3380 let replica_id = this.update(&mut cx, |this, cx| this.replica_id(cx))?;
3381
3382 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
3383 cx.update(|_, cx| {
3384 entries.sort_unstable_by_key(|(buffer, _)| {
3385 buffer.read(cx).file().map(|f| f.path().clone())
3386 });
3387 })?;
3388
3389 // If the project transaction's edits are all contained within this editor, then
3390 // avoid opening a new editor to display them.
3391
3392 if let Some((buffer, transaction)) = entries.first() {
3393 if entries.len() == 1 {
3394 let excerpt = this.update(&mut cx, |editor, cx| {
3395 editor
3396 .buffer()
3397 .read(cx)
3398 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
3399 })?;
3400 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
3401 if excerpted_buffer == *buffer {
3402 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
3403 let excerpt_range = excerpt_range.to_offset(buffer);
3404 buffer
3405 .edited_ranges_for_transaction::<usize>(transaction)
3406 .all(|range| {
3407 excerpt_range.start <= range.start
3408 && excerpt_range.end >= range.end
3409 })
3410 })?;
3411
3412 if all_edits_within_excerpt {
3413 return Ok(());
3414 }
3415 }
3416 }
3417 }
3418 } else {
3419 return Ok(());
3420 }
3421
3422 let mut ranges_to_highlight = Vec::new();
3423 let excerpt_buffer = cx.new_model(|cx| {
3424 let mut multibuffer =
3425 MultiBuffer::new(replica_id, Capability::ReadWrite).with_title(title);
3426 for (buffer_handle, transaction) in &entries {
3427 let buffer = buffer_handle.read(cx);
3428 ranges_to_highlight.extend(
3429 multibuffer.push_excerpts_with_context_lines(
3430 buffer_handle.clone(),
3431 buffer
3432 .edited_ranges_for_transaction::<usize>(transaction)
3433 .collect(),
3434 1,
3435 cx,
3436 ),
3437 );
3438 }
3439 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
3440 multibuffer
3441 })?;
3442
3443 workspace.update(&mut cx, |workspace, cx| {
3444 let project = workspace.project().clone();
3445 let editor =
3446 cx.new_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
3447 workspace.add_item(Box::new(editor.clone()), cx);
3448 editor.update(cx, |editor, cx| {
3449 editor.highlight_background::<Self>(
3450 ranges_to_highlight,
3451 |theme| theme.editor_highlighted_line_background,
3452 cx,
3453 );
3454 });
3455 })?;
3456
3457 Ok(())
3458 }
3459
3460 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3461 let project = self.project.clone()?;
3462 let buffer = self.buffer.read(cx);
3463 let newest_selection = self.selections.newest_anchor().clone();
3464 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
3465 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
3466 if start_buffer != end_buffer {
3467 return None;
3468 }
3469
3470 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
3471 cx.background_executor()
3472 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
3473 .await;
3474
3475 let actions = if let Ok(code_actions) = project.update(&mut cx, |project, cx| {
3476 project.code_actions(&start_buffer, start..end, cx)
3477 }) {
3478 code_actions.await.log_err()
3479 } else {
3480 None
3481 };
3482
3483 this.update(&mut cx, |this, cx| {
3484 this.available_code_actions = actions.and_then(|actions| {
3485 if actions.is_empty() {
3486 None
3487 } else {
3488 Some((start_buffer, actions.into()))
3489 }
3490 });
3491 cx.notify();
3492 })
3493 .log_err();
3494 }));
3495 None
3496 }
3497
3498 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3499 if self.pending_rename.is_some() {
3500 return None;
3501 }
3502
3503 let project = self.project.clone()?;
3504 let buffer = self.buffer.read(cx);
3505 let newest_selection = self.selections.newest_anchor().clone();
3506 let cursor_position = newest_selection.head();
3507 let (cursor_buffer, cursor_buffer_position) =
3508 buffer.text_anchor_for_position(cursor_position.clone(), cx)?;
3509 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
3510 if cursor_buffer != tail_buffer {
3511 return None;
3512 }
3513
3514 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
3515 cx.background_executor()
3516 .timer(DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT)
3517 .await;
3518
3519 let highlights = if let Some(highlights) = project
3520 .update(&mut cx, |project, cx| {
3521 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
3522 })
3523 .log_err()
3524 {
3525 highlights.await.log_err()
3526 } else {
3527 None
3528 };
3529
3530 if let Some(highlights) = highlights {
3531 this.update(&mut cx, |this, cx| {
3532 if this.pending_rename.is_some() {
3533 return;
3534 }
3535
3536 let buffer_id = cursor_position.buffer_id;
3537 let buffer = this.buffer.read(cx);
3538 if !buffer
3539 .text_anchor_for_position(cursor_position, cx)
3540 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
3541 {
3542 return;
3543 }
3544
3545 let cursor_buffer_snapshot = cursor_buffer.read(cx);
3546 let mut write_ranges = Vec::new();
3547 let mut read_ranges = Vec::new();
3548 for highlight in highlights {
3549 for (excerpt_id, excerpt_range) in
3550 buffer.excerpts_for_buffer(&cursor_buffer, cx)
3551 {
3552 let start = highlight
3553 .range
3554 .start
3555 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
3556 let end = highlight
3557 .range
3558 .end
3559 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
3560 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
3561 continue;
3562 }
3563
3564 let range = Anchor {
3565 buffer_id,
3566 excerpt_id: excerpt_id.clone(),
3567 text_anchor: start,
3568 }..Anchor {
3569 buffer_id,
3570 excerpt_id,
3571 text_anchor: end,
3572 };
3573 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
3574 write_ranges.push(range);
3575 } else {
3576 read_ranges.push(range);
3577 }
3578 }
3579 }
3580
3581 this.highlight_background::<DocumentHighlightRead>(
3582 read_ranges,
3583 |theme| theme.editor_document_highlight_read_background,
3584 cx,
3585 );
3586 this.highlight_background::<DocumentHighlightWrite>(
3587 write_ranges,
3588 |theme| theme.editor_document_highlight_write_background,
3589 cx,
3590 );
3591 cx.notify();
3592 })
3593 .log_err();
3594 }
3595 }));
3596 None
3597 }
3598
3599 fn refresh_copilot_suggestions(
3600 &mut self,
3601 debounce: bool,
3602 cx: &mut ViewContext<Self>,
3603 ) -> Option<()> {
3604 let copilot = Copilot::global(cx)?;
3605 if !self.show_copilot_suggestions || !copilot.read(cx).status().is_authorized() {
3606 self.clear_copilot_suggestions(cx);
3607 return None;
3608 }
3609 self.update_visible_copilot_suggestion(cx);
3610
3611 let snapshot = self.buffer.read(cx).snapshot(cx);
3612 let cursor = self.selections.newest_anchor().head();
3613 if !self.is_copilot_enabled_at(cursor, &snapshot, cx) {
3614 self.clear_copilot_suggestions(cx);
3615 return None;
3616 }
3617
3618 let (buffer, buffer_position) =
3619 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
3620 self.copilot_state.pending_refresh = cx.spawn(|this, mut cx| async move {
3621 if debounce {
3622 cx.background_executor()
3623 .timer(COPILOT_DEBOUNCE_TIMEOUT)
3624 .await;
3625 }
3626
3627 let completions = copilot
3628 .update(&mut cx, |copilot, cx| {
3629 copilot.completions(&buffer, buffer_position, cx)
3630 })
3631 .log_err()
3632 .unwrap_or(Task::ready(Ok(Vec::new())))
3633 .await
3634 .log_err()
3635 .into_iter()
3636 .flatten()
3637 .collect_vec();
3638
3639 this.update(&mut cx, |this, cx| {
3640 if !completions.is_empty() {
3641 this.copilot_state.cycled = false;
3642 this.copilot_state.pending_cycling_refresh = Task::ready(None);
3643 this.copilot_state.completions.clear();
3644 this.copilot_state.active_completion_index = 0;
3645 this.copilot_state.excerpt_id = Some(cursor.excerpt_id);
3646 for completion in completions {
3647 this.copilot_state.push_completion(completion);
3648 }
3649 this.update_visible_copilot_suggestion(cx);
3650 }
3651 })
3652 .log_err()?;
3653 Some(())
3654 });
3655
3656 Some(())
3657 }
3658
3659 fn cycle_copilot_suggestions(
3660 &mut self,
3661 direction: Direction,
3662 cx: &mut ViewContext<Self>,
3663 ) -> Option<()> {
3664 let copilot = Copilot::global(cx)?;
3665 if !self.show_copilot_suggestions || !copilot.read(cx).status().is_authorized() {
3666 return None;
3667 }
3668
3669 if self.copilot_state.cycled {
3670 self.copilot_state.cycle_completions(direction);
3671 self.update_visible_copilot_suggestion(cx);
3672 } else {
3673 let cursor = self.selections.newest_anchor().head();
3674 let (buffer, buffer_position) =
3675 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
3676 self.copilot_state.pending_cycling_refresh = cx.spawn(|this, mut cx| async move {
3677 let completions = copilot
3678 .update(&mut cx, |copilot, cx| {
3679 copilot.completions_cycling(&buffer, buffer_position, cx)
3680 })
3681 .log_err()?
3682 .await;
3683
3684 this.update(&mut cx, |this, cx| {
3685 this.copilot_state.cycled = true;
3686 for completion in completions.log_err().into_iter().flatten() {
3687 this.copilot_state.push_completion(completion);
3688 }
3689 this.copilot_state.cycle_completions(direction);
3690 this.update_visible_copilot_suggestion(cx);
3691 })
3692 .log_err()?;
3693
3694 Some(())
3695 });
3696 }
3697
3698 Some(())
3699 }
3700
3701 fn copilot_suggest(&mut self, _: &copilot::Suggest, cx: &mut ViewContext<Self>) {
3702 if !self.has_active_copilot_suggestion(cx) {
3703 self.refresh_copilot_suggestions(false, cx);
3704 return;
3705 }
3706
3707 self.update_visible_copilot_suggestion(cx);
3708 }
3709
3710 pub fn display_cursor_names(&mut self, _: &DisplayCursorNames, cx: &mut ViewContext<Self>) {
3711 self.show_cursor_names(cx);
3712 }
3713
3714 fn show_cursor_names(&mut self, cx: &mut ViewContext<Self>) {
3715 self.show_cursor_names = true;
3716 cx.notify();
3717 cx.spawn(|this, mut cx| async move {
3718 cx.background_executor().timer(Duration::from_secs(2)).await;
3719 this.update(&mut cx, |this, cx| {
3720 this.show_cursor_names = false;
3721 cx.notify()
3722 })
3723 .ok()
3724 })
3725 .detach();
3726 }
3727
3728 fn next_copilot_suggestion(&mut self, _: &copilot::NextSuggestion, cx: &mut ViewContext<Self>) {
3729 if self.has_active_copilot_suggestion(cx) {
3730 self.cycle_copilot_suggestions(Direction::Next, cx);
3731 } else {
3732 let is_copilot_disabled = self.refresh_copilot_suggestions(false, cx).is_none();
3733 if is_copilot_disabled {
3734 cx.propagate();
3735 }
3736 }
3737 }
3738
3739 fn previous_copilot_suggestion(
3740 &mut self,
3741 _: &copilot::PreviousSuggestion,
3742 cx: &mut ViewContext<Self>,
3743 ) {
3744 if self.has_active_copilot_suggestion(cx) {
3745 self.cycle_copilot_suggestions(Direction::Prev, cx);
3746 } else {
3747 let is_copilot_disabled = self.refresh_copilot_suggestions(false, cx).is_none();
3748 if is_copilot_disabled {
3749 cx.propagate();
3750 }
3751 }
3752 }
3753
3754 fn accept_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
3755 if let Some(suggestion) = self.take_active_copilot_suggestion(cx) {
3756 if let Some((copilot, completion)) =
3757 Copilot::global(cx).zip(self.copilot_state.active_completion())
3758 {
3759 copilot
3760 .update(cx, |copilot, cx| copilot.accept_completion(completion, cx))
3761 .detach_and_log_err(cx);
3762
3763 self.report_copilot_event(Some(completion.uuid.clone()), true, cx)
3764 }
3765 cx.emit(EditorEvent::InputHandled {
3766 utf16_range_to_replace: None,
3767 text: suggestion.text.to_string().into(),
3768 });
3769 self.insert_with_autoindent_mode(&suggestion.text.to_string(), None, cx);
3770 cx.notify();
3771 true
3772 } else {
3773 false
3774 }
3775 }
3776
3777 fn discard_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
3778 if let Some(suggestion) = self.take_active_copilot_suggestion(cx) {
3779 if let Some(copilot) = Copilot::global(cx) {
3780 copilot
3781 .update(cx, |copilot, cx| {
3782 copilot.discard_completions(&self.copilot_state.completions, cx)
3783 })
3784 .detach_and_log_err(cx);
3785
3786 self.report_copilot_event(None, false, cx)
3787 }
3788
3789 self.display_map.update(cx, |map, cx| {
3790 map.splice_inlays(vec![suggestion.id], Vec::new(), cx)
3791 });
3792 cx.notify();
3793 true
3794 } else {
3795 false
3796 }
3797 }
3798
3799 fn is_copilot_enabled_at(
3800 &self,
3801 location: Anchor,
3802 snapshot: &MultiBufferSnapshot,
3803 cx: &mut ViewContext<Self>,
3804 ) -> bool {
3805 let file = snapshot.file_at(location);
3806 let language = snapshot.language_at(location);
3807 let settings = all_language_settings(file, cx);
3808 self.show_copilot_suggestions
3809 && settings.copilot_enabled(language, file.map(|f| f.path().as_ref()))
3810 }
3811
3812 fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool {
3813 if let Some(suggestion) = self.copilot_state.suggestion.as_ref() {
3814 let buffer = self.buffer.read(cx).read(cx);
3815 suggestion.position.is_valid(&buffer)
3816 } else {
3817 false
3818 }
3819 }
3820
3821 fn take_active_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> Option<Inlay> {
3822 let suggestion = self.copilot_state.suggestion.take()?;
3823 self.display_map.update(cx, |map, cx| {
3824 map.splice_inlays(vec![suggestion.id], Default::default(), cx);
3825 });
3826 let buffer = self.buffer.read(cx).read(cx);
3827
3828 if suggestion.position.is_valid(&buffer) {
3829 Some(suggestion)
3830 } else {
3831 None
3832 }
3833 }
3834
3835 fn update_visible_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) {
3836 let snapshot = self.buffer.read(cx).snapshot(cx);
3837 let selection = self.selections.newest_anchor();
3838 let cursor = selection.head();
3839
3840 if self.context_menu.read().is_some()
3841 || !self.completion_tasks.is_empty()
3842 || selection.start != selection.end
3843 {
3844 self.discard_copilot_suggestion(cx);
3845 } else if let Some(text) = self
3846 .copilot_state
3847 .text_for_active_completion(cursor, &snapshot)
3848 {
3849 let text = Rope::from(text);
3850 let mut to_remove = Vec::new();
3851 if let Some(suggestion) = self.copilot_state.suggestion.take() {
3852 to_remove.push(suggestion.id);
3853 }
3854
3855 let suggestion_inlay =
3856 Inlay::suggestion(post_inc(&mut self.next_inlay_id), cursor, text);
3857 self.copilot_state.suggestion = Some(suggestion_inlay.clone());
3858 self.display_map.update(cx, move |map, cx| {
3859 map.splice_inlays(to_remove, vec![suggestion_inlay], cx)
3860 });
3861 cx.notify();
3862 } else {
3863 self.discard_copilot_suggestion(cx);
3864 }
3865 }
3866
3867 fn clear_copilot_suggestions(&mut self, cx: &mut ViewContext<Self>) {
3868 self.copilot_state = Default::default();
3869 self.discard_copilot_suggestion(cx);
3870 }
3871
3872 pub fn render_code_actions_indicator(
3873 &self,
3874 _style: &EditorStyle,
3875 is_active: bool,
3876 cx: &mut ViewContext<Self>,
3877 ) -> Option<IconButton> {
3878 if self.available_code_actions.is_some() {
3879 Some(
3880 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
3881 .icon_size(IconSize::Small)
3882 .icon_color(Color::Muted)
3883 .selected(is_active)
3884 .on_click(cx.listener(|editor, _e, cx| {
3885 editor.toggle_code_actions(
3886 &ToggleCodeActions {
3887 deployed_from_indicator: true,
3888 },
3889 cx,
3890 );
3891 })),
3892 )
3893 } else {
3894 None
3895 }
3896 }
3897
3898 pub fn render_fold_indicators(
3899 &self,
3900 fold_data: Vec<Option<(FoldStatus, u32, bool)>>,
3901 _style: &EditorStyle,
3902 gutter_hovered: bool,
3903 _line_height: Pixels,
3904 _gutter_margin: Pixels,
3905 cx: &mut ViewContext<Self>,
3906 ) -> Vec<Option<IconButton>> {
3907 fold_data
3908 .iter()
3909 .enumerate()
3910 .map(|(ix, fold_data)| {
3911 fold_data
3912 .map(|(fold_status, buffer_row, active)| {
3913 (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| {
3914 IconButton::new(ix as usize, ui::IconName::ChevronDown)
3915 .on_click(cx.listener(move |editor, _e, cx| match fold_status {
3916 FoldStatus::Folded => {
3917 editor.unfold_at(&UnfoldAt { buffer_row }, cx);
3918 }
3919 FoldStatus::Foldable => {
3920 editor.fold_at(&FoldAt { buffer_row }, cx);
3921 }
3922 }))
3923 .icon_color(ui::Color::Muted)
3924 .icon_size(ui::IconSize::Small)
3925 .selected(fold_status == FoldStatus::Folded)
3926 .selected_icon(ui::IconName::ChevronRight)
3927 .size(ui::ButtonSize::None)
3928 })
3929 })
3930 .flatten()
3931 })
3932 .collect()
3933 }
3934
3935 pub fn context_menu_visible(&self) -> bool {
3936 self.context_menu
3937 .read()
3938 .as_ref()
3939 .map_or(false, |menu| menu.visible())
3940 }
3941
3942 pub fn render_context_menu(
3943 &self,
3944 cursor_position: DisplayPoint,
3945 style: &EditorStyle,
3946 max_height: Pixels,
3947 cx: &mut ViewContext<Editor>,
3948 ) -> Option<(DisplayPoint, AnyElement)> {
3949 self.context_menu.read().as_ref().map(|menu| {
3950 menu.render(
3951 cursor_position,
3952 style,
3953 max_height,
3954 self.workspace.as_ref().map(|(w, _)| w.clone()),
3955 cx,
3956 )
3957 })
3958 }
3959
3960 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
3961 cx.notify();
3962 self.completion_tasks.clear();
3963 let context_menu = self.context_menu.write().take();
3964 if context_menu.is_some() {
3965 self.update_visible_copilot_suggestion(cx);
3966 }
3967 context_menu
3968 }
3969
3970 pub fn insert_snippet(
3971 &mut self,
3972 insertion_ranges: &[Range<usize>],
3973 snippet: Snippet,
3974 cx: &mut ViewContext<Self>,
3975 ) -> Result<()> {
3976 let tabstops = self.buffer.update(cx, |buffer, cx| {
3977 let snippet_text: Arc<str> = snippet.text.clone().into();
3978 buffer.edit(
3979 insertion_ranges
3980 .iter()
3981 .cloned()
3982 .map(|range| (range, snippet_text.clone())),
3983 Some(AutoindentMode::EachLine),
3984 cx,
3985 );
3986
3987 let snapshot = &*buffer.read(cx);
3988 let snippet = &snippet;
3989 snippet
3990 .tabstops
3991 .iter()
3992 .map(|tabstop| {
3993 let mut tabstop_ranges = tabstop
3994 .iter()
3995 .flat_map(|tabstop_range| {
3996 let mut delta = 0_isize;
3997 insertion_ranges.iter().map(move |insertion_range| {
3998 let insertion_start = insertion_range.start as isize + delta;
3999 delta +=
4000 snippet.text.len() as isize - insertion_range.len() as isize;
4001
4002 let start = snapshot.anchor_before(
4003 (insertion_start + tabstop_range.start) as usize,
4004 );
4005 let end = snapshot
4006 .anchor_after((insertion_start + tabstop_range.end) as usize);
4007 start..end
4008 })
4009 })
4010 .collect::<Vec<_>>();
4011 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
4012 tabstop_ranges
4013 })
4014 .collect::<Vec<_>>()
4015 });
4016
4017 if let Some(tabstop) = tabstops.first() {
4018 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4019 s.select_ranges(tabstop.iter().cloned());
4020 });
4021 self.snippet_stack.push(SnippetState {
4022 active_index: 0,
4023 ranges: tabstops,
4024 });
4025 }
4026
4027 Ok(())
4028 }
4029
4030 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
4031 self.move_to_snippet_tabstop(Bias::Right, cx)
4032 }
4033
4034 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
4035 self.move_to_snippet_tabstop(Bias::Left, cx)
4036 }
4037
4038 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
4039 if let Some(mut snippet) = self.snippet_stack.pop() {
4040 match bias {
4041 Bias::Left => {
4042 if snippet.active_index > 0 {
4043 snippet.active_index -= 1;
4044 } else {
4045 self.snippet_stack.push(snippet);
4046 return false;
4047 }
4048 }
4049 Bias::Right => {
4050 if snippet.active_index + 1 < snippet.ranges.len() {
4051 snippet.active_index += 1;
4052 } else {
4053 self.snippet_stack.push(snippet);
4054 return false;
4055 }
4056 }
4057 }
4058 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
4059 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4060 s.select_anchor_ranges(current_ranges.iter().cloned())
4061 });
4062 // If snippet state is not at the last tabstop, push it back on the stack
4063 if snippet.active_index + 1 < snippet.ranges.len() {
4064 self.snippet_stack.push(snippet);
4065 }
4066 return true;
4067 }
4068 }
4069
4070 false
4071 }
4072
4073 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
4074 self.transact(cx, |this, cx| {
4075 this.select_all(&SelectAll, cx);
4076 this.insert("", cx);
4077 });
4078 }
4079
4080 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
4081 self.transact(cx, |this, cx| {
4082 this.select_autoclose_pair(cx);
4083 let mut selections = this.selections.all::<Point>(cx);
4084 if !this.selections.line_mode {
4085 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
4086 for selection in &mut selections {
4087 if selection.is_empty() {
4088 let old_head = selection.head();
4089 let mut new_head =
4090 movement::left(&display_map, old_head.to_display_point(&display_map))
4091 .to_point(&display_map);
4092 if let Some((buffer, line_buffer_range)) = display_map
4093 .buffer_snapshot
4094 .buffer_line_for_row(old_head.row)
4095 {
4096 let indent_size =
4097 buffer.indent_size_for_line(line_buffer_range.start.row);
4098 let indent_len = match indent_size.kind {
4099 IndentKind::Space => {
4100 buffer.settings_at(line_buffer_range.start, cx).tab_size
4101 }
4102 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
4103 };
4104 if old_head.column <= indent_size.len && old_head.column > 0 {
4105 let indent_len = indent_len.get();
4106 new_head = cmp::min(
4107 new_head,
4108 Point::new(
4109 old_head.row,
4110 ((old_head.column - 1) / indent_len) * indent_len,
4111 ),
4112 );
4113 }
4114 }
4115
4116 selection.set_head(new_head, SelectionGoal::None);
4117 }
4118 }
4119 }
4120
4121 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4122 this.insert("", cx);
4123 this.refresh_copilot_suggestions(true, cx);
4124 });
4125 }
4126
4127 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
4128 self.transact(cx, |this, cx| {
4129 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4130 let line_mode = s.line_mode;
4131 s.move_with(|map, selection| {
4132 if selection.is_empty() && !line_mode {
4133 let cursor = movement::right(map, selection.head());
4134 selection.end = cursor;
4135 selection.reversed = true;
4136 selection.goal = SelectionGoal::None;
4137 }
4138 })
4139 });
4140 this.insert("", cx);
4141 this.refresh_copilot_suggestions(true, cx);
4142 });
4143 }
4144
4145 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
4146 if self.move_to_prev_snippet_tabstop(cx) {
4147 return;
4148 }
4149
4150 self.outdent(&Outdent, cx);
4151 }
4152
4153 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
4154 if self.move_to_next_snippet_tabstop(cx) || self.read_only(cx) {
4155 return;
4156 }
4157
4158 let mut selections = self.selections.all_adjusted(cx);
4159 let buffer = self.buffer.read(cx);
4160 let snapshot = buffer.snapshot(cx);
4161 let rows_iter = selections.iter().map(|s| s.head().row);
4162 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
4163
4164 let mut edits = Vec::new();
4165 let mut prev_edited_row = 0;
4166 let mut row_delta = 0;
4167 for selection in &mut selections {
4168 if selection.start.row != prev_edited_row {
4169 row_delta = 0;
4170 }
4171 prev_edited_row = selection.end.row;
4172
4173 // If the selection is non-empty, then increase the indentation of the selected lines.
4174 if !selection.is_empty() {
4175 row_delta =
4176 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
4177 continue;
4178 }
4179
4180 // If the selection is empty and the cursor is in the leading whitespace before the
4181 // suggested indentation, then auto-indent the line.
4182 let cursor = selection.head();
4183 let current_indent = snapshot.indent_size_for_line(cursor.row);
4184 if let Some(suggested_indent) = suggested_indents.get(&cursor.row).copied() {
4185 if cursor.column < suggested_indent.len
4186 && cursor.column <= current_indent.len
4187 && current_indent.len <= suggested_indent.len
4188 {
4189 selection.start = Point::new(cursor.row, suggested_indent.len);
4190 selection.end = selection.start;
4191 if row_delta == 0 {
4192 edits.extend(Buffer::edit_for_indent_size_adjustment(
4193 cursor.row,
4194 current_indent,
4195 suggested_indent,
4196 ));
4197 row_delta = suggested_indent.len - current_indent.len;
4198 }
4199 continue;
4200 }
4201 }
4202
4203 // Accept copilot suggestion if there is only one selection and the cursor is not
4204 // in the leading whitespace.
4205 if self.selections.count() == 1
4206 && cursor.column >= current_indent.len
4207 && self.has_active_copilot_suggestion(cx)
4208 {
4209 self.accept_copilot_suggestion(cx);
4210 return;
4211 }
4212
4213 // Otherwise, insert a hard or soft tab.
4214 let settings = buffer.settings_at(cursor, cx);
4215 let tab_size = if settings.hard_tabs {
4216 IndentSize::tab()
4217 } else {
4218 let tab_size = settings.tab_size.get();
4219 let char_column = snapshot
4220 .text_for_range(Point::new(cursor.row, 0)..cursor)
4221 .flat_map(str::chars)
4222 .count()
4223 + row_delta as usize;
4224 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
4225 IndentSize::spaces(chars_to_next_tab_stop)
4226 };
4227 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
4228 selection.end = selection.start;
4229 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
4230 row_delta += tab_size.len;
4231 }
4232
4233 self.transact(cx, |this, cx| {
4234 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
4235 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4236 this.refresh_copilot_suggestions(true, cx);
4237 });
4238 }
4239
4240 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
4241 let mut selections = self.selections.all::<Point>(cx);
4242 let mut prev_edited_row = 0;
4243 let mut row_delta = 0;
4244 let mut edits = Vec::new();
4245 let buffer = self.buffer.read(cx);
4246 let snapshot = buffer.snapshot(cx);
4247 for selection in &mut selections {
4248 if selection.start.row != prev_edited_row {
4249 row_delta = 0;
4250 }
4251 prev_edited_row = selection.end.row;
4252
4253 row_delta =
4254 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
4255 }
4256
4257 self.transact(cx, |this, cx| {
4258 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
4259 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4260 });
4261 }
4262
4263 fn indent_selection(
4264 buffer: &MultiBuffer,
4265 snapshot: &MultiBufferSnapshot,
4266 selection: &mut Selection<Point>,
4267 edits: &mut Vec<(Range<Point>, String)>,
4268 delta_for_start_row: u32,
4269 cx: &AppContext,
4270 ) -> u32 {
4271 let settings = buffer.settings_at(selection.start, cx);
4272 let tab_size = settings.tab_size.get();
4273 let indent_kind = if settings.hard_tabs {
4274 IndentKind::Tab
4275 } else {
4276 IndentKind::Space
4277 };
4278 let mut start_row = selection.start.row;
4279 let mut end_row = selection.end.row + 1;
4280
4281 // If a selection ends at the beginning of a line, don't indent
4282 // that last line.
4283 if selection.end.column == 0 {
4284 end_row -= 1;
4285 }
4286
4287 // Avoid re-indenting a row that has already been indented by a
4288 // previous selection, but still update this selection's column
4289 // to reflect that indentation.
4290 if delta_for_start_row > 0 {
4291 start_row += 1;
4292 selection.start.column += delta_for_start_row;
4293 if selection.end.row == selection.start.row {
4294 selection.end.column += delta_for_start_row;
4295 }
4296 }
4297
4298 let mut delta_for_end_row = 0;
4299 for row in start_row..end_row {
4300 let current_indent = snapshot.indent_size_for_line(row);
4301 let indent_delta = match (current_indent.kind, indent_kind) {
4302 (IndentKind::Space, IndentKind::Space) => {
4303 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
4304 IndentSize::spaces(columns_to_next_tab_stop)
4305 }
4306 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
4307 (_, IndentKind::Tab) => IndentSize::tab(),
4308 };
4309
4310 let row_start = Point::new(row, 0);
4311 edits.push((
4312 row_start..row_start,
4313 indent_delta.chars().collect::<String>(),
4314 ));
4315
4316 // Update this selection's endpoints to reflect the indentation.
4317 if row == selection.start.row {
4318 selection.start.column += indent_delta.len;
4319 }
4320 if row == selection.end.row {
4321 selection.end.column += indent_delta.len;
4322 delta_for_end_row = indent_delta.len;
4323 }
4324 }
4325
4326 if selection.start.row == selection.end.row {
4327 delta_for_start_row + delta_for_end_row
4328 } else {
4329 delta_for_end_row
4330 }
4331 }
4332
4333 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
4334 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4335 let selections = self.selections.all::<Point>(cx);
4336 let mut deletion_ranges = Vec::new();
4337 let mut last_outdent = None;
4338 {
4339 let buffer = self.buffer.read(cx);
4340 let snapshot = buffer.snapshot(cx);
4341 for selection in &selections {
4342 let settings = buffer.settings_at(selection.start, cx);
4343 let tab_size = settings.tab_size.get();
4344 let mut rows = selection.spanned_rows(false, &display_map);
4345
4346 // Avoid re-outdenting a row that has already been outdented by a
4347 // previous selection.
4348 if let Some(last_row) = last_outdent {
4349 if last_row == rows.start {
4350 rows.start += 1;
4351 }
4352 }
4353
4354 for row in rows {
4355 let indent_size = snapshot.indent_size_for_line(row);
4356 if indent_size.len > 0 {
4357 let deletion_len = match indent_size.kind {
4358 IndentKind::Space => {
4359 let columns_to_prev_tab_stop = indent_size.len % tab_size;
4360 if columns_to_prev_tab_stop == 0 {
4361 tab_size
4362 } else {
4363 columns_to_prev_tab_stop
4364 }
4365 }
4366 IndentKind::Tab => 1,
4367 };
4368 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
4369 last_outdent = Some(row);
4370 }
4371 }
4372 }
4373 }
4374
4375 self.transact(cx, |this, cx| {
4376 this.buffer.update(cx, |buffer, cx| {
4377 let empty_str: Arc<str> = "".into();
4378 buffer.edit(
4379 deletion_ranges
4380 .into_iter()
4381 .map(|range| (range, empty_str.clone())),
4382 None,
4383 cx,
4384 );
4385 });
4386 let selections = this.selections.all::<usize>(cx);
4387 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4388 });
4389 }
4390
4391 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
4392 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4393 let selections = self.selections.all::<Point>(cx);
4394
4395 let mut new_cursors = Vec::new();
4396 let mut edit_ranges = Vec::new();
4397 let mut selections = selections.iter().peekable();
4398 while let Some(selection) = selections.next() {
4399 let mut rows = selection.spanned_rows(false, &display_map);
4400 let goal_display_column = selection.head().to_display_point(&display_map).column();
4401
4402 // Accumulate contiguous regions of rows that we want to delete.
4403 while let Some(next_selection) = selections.peek() {
4404 let next_rows = next_selection.spanned_rows(false, &display_map);
4405 if next_rows.start <= rows.end {
4406 rows.end = next_rows.end;
4407 selections.next().unwrap();
4408 } else {
4409 break;
4410 }
4411 }
4412
4413 let buffer = &display_map.buffer_snapshot;
4414 let mut edit_start = Point::new(rows.start, 0).to_offset(buffer);
4415 let edit_end;
4416 let cursor_buffer_row;
4417 if buffer.max_point().row >= rows.end {
4418 // If there's a line after the range, delete the \n from the end of the row range
4419 // and position the cursor on the next line.
4420 edit_end = Point::new(rows.end, 0).to_offset(buffer);
4421 cursor_buffer_row = rows.end;
4422 } else {
4423 // If there isn't a line after the range, delete the \n from the line before the
4424 // start of the row range and position the cursor there.
4425 edit_start = edit_start.saturating_sub(1);
4426 edit_end = buffer.len();
4427 cursor_buffer_row = rows.start.saturating_sub(1);
4428 }
4429
4430 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
4431 *cursor.column_mut() =
4432 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
4433
4434 new_cursors.push((
4435 selection.id,
4436 buffer.anchor_after(cursor.to_point(&display_map)),
4437 ));
4438 edit_ranges.push(edit_start..edit_end);
4439 }
4440
4441 self.transact(cx, |this, cx| {
4442 let buffer = this.buffer.update(cx, |buffer, cx| {
4443 let empty_str: Arc<str> = "".into();
4444 buffer.edit(
4445 edit_ranges
4446 .into_iter()
4447 .map(|range| (range, empty_str.clone())),
4448 None,
4449 cx,
4450 );
4451 buffer.snapshot(cx)
4452 });
4453 let new_selections = new_cursors
4454 .into_iter()
4455 .map(|(id, cursor)| {
4456 let cursor = cursor.to_point(&buffer);
4457 Selection {
4458 id,
4459 start: cursor,
4460 end: cursor,
4461 reversed: false,
4462 goal: SelectionGoal::None,
4463 }
4464 })
4465 .collect();
4466
4467 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4468 s.select(new_selections);
4469 });
4470 });
4471 }
4472
4473 pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
4474 let mut row_ranges = Vec::<Range<u32>>::new();
4475 for selection in self.selections.all::<Point>(cx) {
4476 let start = selection.start.row;
4477 let end = if selection.start.row == selection.end.row {
4478 selection.start.row + 1
4479 } else {
4480 selection.end.row
4481 };
4482
4483 if let Some(last_row_range) = row_ranges.last_mut() {
4484 if start <= last_row_range.end {
4485 last_row_range.end = end;
4486 continue;
4487 }
4488 }
4489 row_ranges.push(start..end);
4490 }
4491
4492 let snapshot = self.buffer.read(cx).snapshot(cx);
4493 let mut cursor_positions = Vec::new();
4494 for row_range in &row_ranges {
4495 let anchor = snapshot.anchor_before(Point::new(
4496 row_range.end - 1,
4497 snapshot.line_len(row_range.end - 1),
4498 ));
4499 cursor_positions.push(anchor.clone()..anchor);
4500 }
4501
4502 self.transact(cx, |this, cx| {
4503 for row_range in row_ranges.into_iter().rev() {
4504 for row in row_range.rev() {
4505 let end_of_line = Point::new(row, snapshot.line_len(row));
4506 let indent = snapshot.indent_size_for_line(row + 1);
4507 let start_of_next_line = Point::new(row + 1, indent.len);
4508
4509 let replace = if snapshot.line_len(row + 1) > indent.len {
4510 " "
4511 } else {
4512 ""
4513 };
4514
4515 this.buffer.update(cx, |buffer, cx| {
4516 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
4517 });
4518 }
4519 }
4520
4521 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4522 s.select_anchor_ranges(cursor_positions)
4523 });
4524 });
4525 }
4526
4527 pub fn sort_lines_case_sensitive(
4528 &mut self,
4529 _: &SortLinesCaseSensitive,
4530 cx: &mut ViewContext<Self>,
4531 ) {
4532 self.manipulate_lines(cx, |lines| lines.sort())
4533 }
4534
4535 pub fn sort_lines_case_insensitive(
4536 &mut self,
4537 _: &SortLinesCaseInsensitive,
4538 cx: &mut ViewContext<Self>,
4539 ) {
4540 self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase()))
4541 }
4542
4543 pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
4544 self.manipulate_lines(cx, |lines| lines.reverse())
4545 }
4546
4547 pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext<Self>) {
4548 self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng()))
4549 }
4550
4551 fn manipulate_lines<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
4552 where
4553 Fn: FnMut(&mut [&str]),
4554 {
4555 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4556 let buffer = self.buffer.read(cx).snapshot(cx);
4557
4558 let mut edits = Vec::new();
4559
4560 let selections = self.selections.all::<Point>(cx);
4561 let mut selections = selections.iter().peekable();
4562 let mut contiguous_row_selections = Vec::new();
4563 let mut new_selections = Vec::new();
4564
4565 while let Some(selection) = selections.next() {
4566 let (start_row, end_row) = consume_contiguous_rows(
4567 &mut contiguous_row_selections,
4568 selection,
4569 &display_map,
4570 &mut selections,
4571 );
4572
4573 let start_point = Point::new(start_row, 0);
4574 let end_point = Point::new(end_row - 1, buffer.line_len(end_row - 1));
4575 let text = buffer
4576 .text_for_range(start_point..end_point)
4577 .collect::<String>();
4578 let mut lines = text.split("\n").collect_vec();
4579
4580 let lines_len = lines.len();
4581 callback(&mut lines);
4582
4583 // This is a current limitation with selections.
4584 // If we wanted to support removing or adding lines, we'd need to fix the logic associated with selections.
4585 debug_assert!(
4586 lines.len() == lines_len,
4587 "callback should not change the number of lines"
4588 );
4589
4590 edits.push((start_point..end_point, lines.join("\n")));
4591 let start_anchor = buffer.anchor_after(start_point);
4592 let end_anchor = buffer.anchor_before(end_point);
4593
4594 // Make selection and push
4595 new_selections.push(Selection {
4596 id: selection.id,
4597 start: start_anchor.to_offset(&buffer),
4598 end: end_anchor.to_offset(&buffer),
4599 goal: SelectionGoal::None,
4600 reversed: selection.reversed,
4601 });
4602 }
4603
4604 self.transact(cx, |this, cx| {
4605 this.buffer.update(cx, |buffer, cx| {
4606 buffer.edit(edits, None, cx);
4607 });
4608
4609 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4610 s.select(new_selections);
4611 });
4612
4613 this.request_autoscroll(Autoscroll::fit(), cx);
4614 });
4615 }
4616
4617 pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
4618 self.manipulate_text(cx, |text| text.to_uppercase())
4619 }
4620
4621 pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
4622 self.manipulate_text(cx, |text| text.to_lowercase())
4623 }
4624
4625 pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext<Self>) {
4626 self.manipulate_text(cx, |text| {
4627 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
4628 // https://github.com/rutrum/convert-case/issues/16
4629 text.split("\n")
4630 .map(|line| line.to_case(Case::Title))
4631 .join("\n")
4632 })
4633 }
4634
4635 pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
4636 self.manipulate_text(cx, |text| text.to_case(Case::Snake))
4637 }
4638
4639 pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
4640 self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
4641 }
4642
4643 pub fn convert_to_upper_camel_case(
4644 &mut self,
4645 _: &ConvertToUpperCamelCase,
4646 cx: &mut ViewContext<Self>,
4647 ) {
4648 self.manipulate_text(cx, |text| {
4649 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
4650 // https://github.com/rutrum/convert-case/issues/16
4651 text.split("\n")
4652 .map(|line| line.to_case(Case::UpperCamel))
4653 .join("\n")
4654 })
4655 }
4656
4657 pub fn convert_to_lower_camel_case(
4658 &mut self,
4659 _: &ConvertToLowerCamelCase,
4660 cx: &mut ViewContext<Self>,
4661 ) {
4662 self.manipulate_text(cx, |text| text.to_case(Case::Camel))
4663 }
4664
4665 fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
4666 where
4667 Fn: FnMut(&str) -> String,
4668 {
4669 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4670 let buffer = self.buffer.read(cx).snapshot(cx);
4671
4672 let mut new_selections = Vec::new();
4673 let mut edits = Vec::new();
4674 let mut selection_adjustment = 0i32;
4675
4676 for selection in self.selections.all::<usize>(cx) {
4677 let selection_is_empty = selection.is_empty();
4678
4679 let (start, end) = if selection_is_empty {
4680 let word_range = movement::surrounding_word(
4681 &display_map,
4682 selection.start.to_display_point(&display_map),
4683 );
4684 let start = word_range.start.to_offset(&display_map, Bias::Left);
4685 let end = word_range.end.to_offset(&display_map, Bias::Left);
4686 (start, end)
4687 } else {
4688 (selection.start, selection.end)
4689 };
4690
4691 let text = buffer.text_for_range(start..end).collect::<String>();
4692 let old_length = text.len() as i32;
4693 let text = callback(&text);
4694
4695 new_selections.push(Selection {
4696 start: (start as i32 - selection_adjustment) as usize,
4697 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
4698 goal: SelectionGoal::None,
4699 ..selection
4700 });
4701
4702 selection_adjustment += old_length - text.len() as i32;
4703
4704 edits.push((start..end, text));
4705 }
4706
4707 self.transact(cx, |this, cx| {
4708 this.buffer.update(cx, |buffer, cx| {
4709 buffer.edit(edits, None, cx);
4710 });
4711
4712 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4713 s.select(new_selections);
4714 });
4715
4716 this.request_autoscroll(Autoscroll::fit(), cx);
4717 });
4718 }
4719
4720 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
4721 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4722 let buffer = &display_map.buffer_snapshot;
4723 let selections = self.selections.all::<Point>(cx);
4724
4725 let mut edits = Vec::new();
4726 let mut selections_iter = selections.iter().peekable();
4727 while let Some(selection) = selections_iter.next() {
4728 // Avoid duplicating the same lines twice.
4729 let mut rows = selection.spanned_rows(false, &display_map);
4730
4731 while let Some(next_selection) = selections_iter.peek() {
4732 let next_rows = next_selection.spanned_rows(false, &display_map);
4733 if next_rows.start < rows.end {
4734 rows.end = next_rows.end;
4735 selections_iter.next().unwrap();
4736 } else {
4737 break;
4738 }
4739 }
4740
4741 // Copy the text from the selected row region and splice it at the start of the region.
4742 let start = Point::new(rows.start, 0);
4743 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
4744 let text = buffer
4745 .text_for_range(start..end)
4746 .chain(Some("\n"))
4747 .collect::<String>();
4748 edits.push((start..start, text));
4749 }
4750
4751 self.transact(cx, |this, cx| {
4752 this.buffer.update(cx, |buffer, cx| {
4753 buffer.edit(edits, None, cx);
4754 });
4755
4756 this.request_autoscroll(Autoscroll::fit(), cx);
4757 });
4758 }
4759
4760 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
4761 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4762 let buffer = self.buffer.read(cx).snapshot(cx);
4763
4764 let mut edits = Vec::new();
4765 let mut unfold_ranges = Vec::new();
4766 let mut refold_ranges = Vec::new();
4767
4768 let selections = self.selections.all::<Point>(cx);
4769 let mut selections = selections.iter().peekable();
4770 let mut contiguous_row_selections = Vec::new();
4771 let mut new_selections = Vec::new();
4772
4773 while let Some(selection) = selections.next() {
4774 // Find all the selections that span a contiguous row range
4775 let (start_row, end_row) = consume_contiguous_rows(
4776 &mut contiguous_row_selections,
4777 selection,
4778 &display_map,
4779 &mut selections,
4780 );
4781
4782 // Move the text spanned by the row range to be before the line preceding the row range
4783 if start_row > 0 {
4784 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
4785 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
4786 let insertion_point = display_map
4787 .prev_line_boundary(Point::new(start_row - 1, 0))
4788 .0;
4789
4790 // Don't move lines across excerpts
4791 if buffer
4792 .excerpt_boundaries_in_range((
4793 Bound::Excluded(insertion_point),
4794 Bound::Included(range_to_move.end),
4795 ))
4796 .next()
4797 .is_none()
4798 {
4799 let text = buffer
4800 .text_for_range(range_to_move.clone())
4801 .flat_map(|s| s.chars())
4802 .skip(1)
4803 .chain(['\n'])
4804 .collect::<String>();
4805
4806 edits.push((
4807 buffer.anchor_after(range_to_move.start)
4808 ..buffer.anchor_before(range_to_move.end),
4809 String::new(),
4810 ));
4811 let insertion_anchor = buffer.anchor_after(insertion_point);
4812 edits.push((insertion_anchor..insertion_anchor, text));
4813
4814 let row_delta = range_to_move.start.row - insertion_point.row + 1;
4815
4816 // Move selections up
4817 new_selections.extend(contiguous_row_selections.drain(..).map(
4818 |mut selection| {
4819 selection.start.row -= row_delta;
4820 selection.end.row -= row_delta;
4821 selection
4822 },
4823 ));
4824
4825 // Move folds up
4826 unfold_ranges.push(range_to_move.clone());
4827 for fold in display_map.folds_in_range(
4828 buffer.anchor_before(range_to_move.start)
4829 ..buffer.anchor_after(range_to_move.end),
4830 ) {
4831 let mut start = fold.range.start.to_point(&buffer);
4832 let mut end = fold.range.end.to_point(&buffer);
4833 start.row -= row_delta;
4834 end.row -= row_delta;
4835 refold_ranges.push(start..end);
4836 }
4837 }
4838 }
4839
4840 // If we didn't move line(s), preserve the existing selections
4841 new_selections.append(&mut contiguous_row_selections);
4842 }
4843
4844 self.transact(cx, |this, cx| {
4845 this.unfold_ranges(unfold_ranges, true, true, cx);
4846 this.buffer.update(cx, |buffer, cx| {
4847 for (range, text) in edits {
4848 buffer.edit([(range, text)], None, cx);
4849 }
4850 });
4851 this.fold_ranges(refold_ranges, true, cx);
4852 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4853 s.select(new_selections);
4854 })
4855 });
4856 }
4857
4858 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
4859 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4860 let buffer = self.buffer.read(cx).snapshot(cx);
4861
4862 let mut edits = Vec::new();
4863 let mut unfold_ranges = Vec::new();
4864 let mut refold_ranges = Vec::new();
4865
4866 let selections = self.selections.all::<Point>(cx);
4867 let mut selections = selections.iter().peekable();
4868 let mut contiguous_row_selections = Vec::new();
4869 let mut new_selections = Vec::new();
4870
4871 while let Some(selection) = selections.next() {
4872 // Find all the selections that span a contiguous row range
4873 let (start_row, end_row) = consume_contiguous_rows(
4874 &mut contiguous_row_selections,
4875 selection,
4876 &display_map,
4877 &mut selections,
4878 );
4879
4880 // Move the text spanned by the row range to be after the last line of the row range
4881 if end_row <= buffer.max_point().row {
4882 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
4883 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
4884
4885 // Don't move lines across excerpt boundaries
4886 if buffer
4887 .excerpt_boundaries_in_range((
4888 Bound::Excluded(range_to_move.start),
4889 Bound::Included(insertion_point),
4890 ))
4891 .next()
4892 .is_none()
4893 {
4894 let mut text = String::from("\n");
4895 text.extend(buffer.text_for_range(range_to_move.clone()));
4896 text.pop(); // Drop trailing newline
4897 edits.push((
4898 buffer.anchor_after(range_to_move.start)
4899 ..buffer.anchor_before(range_to_move.end),
4900 String::new(),
4901 ));
4902 let insertion_anchor = buffer.anchor_after(insertion_point);
4903 edits.push((insertion_anchor..insertion_anchor, text));
4904
4905 let row_delta = insertion_point.row - range_to_move.end.row + 1;
4906
4907 // Move selections down
4908 new_selections.extend(contiguous_row_selections.drain(..).map(
4909 |mut selection| {
4910 selection.start.row += row_delta;
4911 selection.end.row += row_delta;
4912 selection
4913 },
4914 ));
4915
4916 // Move folds down
4917 unfold_ranges.push(range_to_move.clone());
4918 for fold in display_map.folds_in_range(
4919 buffer.anchor_before(range_to_move.start)
4920 ..buffer.anchor_after(range_to_move.end),
4921 ) {
4922 let mut start = fold.range.start.to_point(&buffer);
4923 let mut end = fold.range.end.to_point(&buffer);
4924 start.row += row_delta;
4925 end.row += row_delta;
4926 refold_ranges.push(start..end);
4927 }
4928 }
4929 }
4930
4931 // If we didn't move line(s), preserve the existing selections
4932 new_selections.append(&mut contiguous_row_selections);
4933 }
4934
4935 self.transact(cx, |this, cx| {
4936 this.unfold_ranges(unfold_ranges, true, true, cx);
4937 this.buffer.update(cx, |buffer, cx| {
4938 for (range, text) in edits {
4939 buffer.edit([(range, text)], None, cx);
4940 }
4941 });
4942 this.fold_ranges(refold_ranges, true, cx);
4943 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
4944 });
4945 }
4946
4947 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
4948 let text_layout_details = &self.text_layout_details(cx);
4949 self.transact(cx, |this, cx| {
4950 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4951 let mut edits: Vec<(Range<usize>, String)> = Default::default();
4952 let line_mode = s.line_mode;
4953 s.move_with(|display_map, selection| {
4954 if !selection.is_empty() || line_mode {
4955 return;
4956 }
4957
4958 let mut head = selection.head();
4959 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
4960 if head.column() == display_map.line_len(head.row()) {
4961 transpose_offset = display_map
4962 .buffer_snapshot
4963 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
4964 }
4965
4966 if transpose_offset == 0 {
4967 return;
4968 }
4969
4970 *head.column_mut() += 1;
4971 head = display_map.clip_point(head, Bias::Right);
4972 let goal = SelectionGoal::HorizontalPosition(
4973 display_map
4974 .x_for_display_point(head, &text_layout_details)
4975 .into(),
4976 );
4977 selection.collapse_to(head, goal);
4978
4979 let transpose_start = display_map
4980 .buffer_snapshot
4981 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
4982 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
4983 let transpose_end = display_map
4984 .buffer_snapshot
4985 .clip_offset(transpose_offset + 1, Bias::Right);
4986 if let Some(ch) =
4987 display_map.buffer_snapshot.chars_at(transpose_start).next()
4988 {
4989 edits.push((transpose_start..transpose_offset, String::new()));
4990 edits.push((transpose_end..transpose_end, ch.to_string()));
4991 }
4992 }
4993 });
4994 edits
4995 });
4996 this.buffer
4997 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
4998 let selections = this.selections.all::<usize>(cx);
4999 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5000 s.select(selections);
5001 });
5002 });
5003 }
5004
5005 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
5006 let mut text = String::new();
5007 let buffer = self.buffer.read(cx).snapshot(cx);
5008 let mut selections = self.selections.all::<Point>(cx);
5009 let mut clipboard_selections = Vec::with_capacity(selections.len());
5010 {
5011 let max_point = buffer.max_point();
5012 let mut is_first = true;
5013 for selection in &mut selections {
5014 let is_entire_line = selection.is_empty() || self.selections.line_mode;
5015 if is_entire_line {
5016 selection.start = Point::new(selection.start.row, 0);
5017 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
5018 selection.goal = SelectionGoal::None;
5019 }
5020 if is_first {
5021 is_first = false;
5022 } else {
5023 text += "\n";
5024 }
5025 let mut len = 0;
5026 for chunk in buffer.text_for_range(selection.start..selection.end) {
5027 text.push_str(chunk);
5028 len += chunk.len();
5029 }
5030 clipboard_selections.push(ClipboardSelection {
5031 len,
5032 is_entire_line,
5033 first_line_indent: buffer.indent_size_for_line(selection.start.row).len,
5034 });
5035 }
5036 }
5037
5038 self.transact(cx, |this, cx| {
5039 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5040 s.select(selections);
5041 });
5042 this.insert("", cx);
5043 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
5044 });
5045 }
5046
5047 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
5048 let selections = self.selections.all::<Point>(cx);
5049 let buffer = self.buffer.read(cx).read(cx);
5050 let mut text = String::new();
5051
5052 let mut clipboard_selections = Vec::with_capacity(selections.len());
5053 {
5054 let max_point = buffer.max_point();
5055 let mut is_first = true;
5056 for selection in selections.iter() {
5057 let mut start = selection.start;
5058 let mut end = selection.end;
5059 let is_entire_line = selection.is_empty() || self.selections.line_mode;
5060 if is_entire_line {
5061 start = Point::new(start.row, 0);
5062 end = cmp::min(max_point, Point::new(end.row + 1, 0));
5063 }
5064 if is_first {
5065 is_first = false;
5066 } else {
5067 text += "\n";
5068 }
5069 let mut len = 0;
5070 for chunk in buffer.text_for_range(start..end) {
5071 text.push_str(chunk);
5072 len += chunk.len();
5073 }
5074 clipboard_selections.push(ClipboardSelection {
5075 len,
5076 is_entire_line,
5077 first_line_indent: buffer.indent_size_for_line(start.row).len,
5078 });
5079 }
5080 }
5081
5082 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
5083 }
5084
5085 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
5086 if self.read_only(cx) {
5087 return;
5088 }
5089
5090 self.transact(cx, |this, cx| {
5091 if let Some(item) = cx.read_from_clipboard() {
5092 let clipboard_text = Cow::Borrowed(item.text());
5093 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
5094 let old_selections = this.selections.all::<usize>(cx);
5095 let all_selections_were_entire_line =
5096 clipboard_selections.iter().all(|s| s.is_entire_line);
5097 let first_selection_indent_column =
5098 clipboard_selections.first().map(|s| s.first_line_indent);
5099 if clipboard_selections.len() != old_selections.len() {
5100 clipboard_selections.drain(..);
5101 }
5102
5103 this.buffer.update(cx, |buffer, cx| {
5104 let snapshot = buffer.read(cx);
5105 let mut start_offset = 0;
5106 let mut edits = Vec::new();
5107 let mut original_indent_columns = Vec::new();
5108 let line_mode = this.selections.line_mode;
5109 for (ix, selection) in old_selections.iter().enumerate() {
5110 let to_insert;
5111 let entire_line;
5112 let original_indent_column;
5113 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
5114 let end_offset = start_offset + clipboard_selection.len;
5115 to_insert = &clipboard_text[start_offset..end_offset];
5116 entire_line = clipboard_selection.is_entire_line;
5117 start_offset = end_offset + 1;
5118 original_indent_column =
5119 Some(clipboard_selection.first_line_indent);
5120 } else {
5121 to_insert = clipboard_text.as_str();
5122 entire_line = all_selections_were_entire_line;
5123 original_indent_column = first_selection_indent_column
5124 }
5125
5126 // If the corresponding selection was empty when this slice of the
5127 // clipboard text was written, then the entire line containing the
5128 // selection was copied. If this selection is also currently empty,
5129 // then paste the line before the current line of the buffer.
5130 let range = if selection.is_empty() && !line_mode && entire_line {
5131 let column = selection.start.to_point(&snapshot).column as usize;
5132 let line_start = selection.start - column;
5133 line_start..line_start
5134 } else {
5135 selection.range()
5136 };
5137
5138 edits.push((range, to_insert));
5139 original_indent_columns.extend(original_indent_column);
5140 }
5141 drop(snapshot);
5142
5143 buffer.edit(
5144 edits,
5145 Some(AutoindentMode::Block {
5146 original_indent_columns,
5147 }),
5148 cx,
5149 );
5150 });
5151
5152 let selections = this.selections.all::<usize>(cx);
5153 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5154 } else {
5155 this.insert(&clipboard_text, cx);
5156 }
5157 }
5158 });
5159 }
5160
5161 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
5162 if self.read_only(cx) {
5163 return;
5164 }
5165
5166 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
5167 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
5168 self.change_selections(None, cx, |s| {
5169 s.select_anchors(selections.to_vec());
5170 });
5171 }
5172 self.request_autoscroll(Autoscroll::fit(), cx);
5173 self.unmark_text(cx);
5174 self.refresh_copilot_suggestions(true, cx);
5175 cx.emit(EditorEvent::Edited);
5176 }
5177 }
5178
5179 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
5180 if self.read_only(cx) {
5181 return;
5182 }
5183
5184 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
5185 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
5186 {
5187 self.change_selections(None, cx, |s| {
5188 s.select_anchors(selections.to_vec());
5189 });
5190 }
5191 self.request_autoscroll(Autoscroll::fit(), cx);
5192 self.unmark_text(cx);
5193 self.refresh_copilot_suggestions(true, cx);
5194 cx.emit(EditorEvent::Edited);
5195 }
5196 }
5197
5198 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
5199 self.buffer
5200 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
5201 }
5202
5203 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
5204 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5205 let line_mode = s.line_mode;
5206 s.move_with(|map, selection| {
5207 let cursor = if selection.is_empty() && !line_mode {
5208 movement::left(map, selection.start)
5209 } else {
5210 selection.start
5211 };
5212 selection.collapse_to(cursor, SelectionGoal::None);
5213 });
5214 })
5215 }
5216
5217 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
5218 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5219 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
5220 })
5221 }
5222
5223 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
5224 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5225 let line_mode = s.line_mode;
5226 s.move_with(|map, selection| {
5227 let cursor = if selection.is_empty() && !line_mode {
5228 movement::right(map, selection.end)
5229 } else {
5230 selection.end
5231 };
5232 selection.collapse_to(cursor, SelectionGoal::None)
5233 });
5234 })
5235 }
5236
5237 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
5238 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5239 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
5240 })
5241 }
5242
5243 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
5244 if self.take_rename(true, cx).is_some() {
5245 return;
5246 }
5247
5248 if matches!(self.mode, EditorMode::SingleLine) {
5249 cx.propagate();
5250 return;
5251 }
5252
5253 let text_layout_details = &self.text_layout_details(cx);
5254
5255 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5256 let line_mode = s.line_mode;
5257 s.move_with(|map, selection| {
5258 if !selection.is_empty() && !line_mode {
5259 selection.goal = SelectionGoal::None;
5260 }
5261 let (cursor, goal) = movement::up(
5262 map,
5263 selection.start,
5264 selection.goal,
5265 false,
5266 &text_layout_details,
5267 );
5268 selection.collapse_to(cursor, goal);
5269 });
5270 })
5271 }
5272
5273 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
5274 if self.take_rename(true, cx).is_some() {
5275 return;
5276 }
5277
5278 if matches!(self.mode, EditorMode::SingleLine) {
5279 cx.propagate();
5280 return;
5281 }
5282
5283 let row_count = if let Some(row_count) = self.visible_line_count() {
5284 row_count as u32 - 1
5285 } else {
5286 return;
5287 };
5288
5289 let autoscroll = if action.center_cursor {
5290 Autoscroll::center()
5291 } else {
5292 Autoscroll::fit()
5293 };
5294
5295 let text_layout_details = &self.text_layout_details(cx);
5296
5297 self.change_selections(Some(autoscroll), cx, |s| {
5298 let line_mode = s.line_mode;
5299 s.move_with(|map, selection| {
5300 if !selection.is_empty() && !line_mode {
5301 selection.goal = SelectionGoal::None;
5302 }
5303 let (cursor, goal) = movement::up_by_rows(
5304 map,
5305 selection.end,
5306 row_count,
5307 selection.goal,
5308 false,
5309 &text_layout_details,
5310 );
5311 selection.collapse_to(cursor, goal);
5312 });
5313 });
5314 }
5315
5316 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
5317 let text_layout_details = &self.text_layout_details(cx);
5318 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5319 s.move_heads_with(|map, head, goal| {
5320 movement::up(map, head, goal, false, &text_layout_details)
5321 })
5322 })
5323 }
5324
5325 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
5326 self.take_rename(true, cx);
5327
5328 if self.mode == EditorMode::SingleLine {
5329 cx.propagate();
5330 return;
5331 }
5332
5333 let text_layout_details = &self.text_layout_details(cx);
5334 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5335 let line_mode = s.line_mode;
5336 s.move_with(|map, selection| {
5337 if !selection.is_empty() && !line_mode {
5338 selection.goal = SelectionGoal::None;
5339 }
5340 let (cursor, goal) = movement::down(
5341 map,
5342 selection.end,
5343 selection.goal,
5344 false,
5345 &text_layout_details,
5346 );
5347 selection.collapse_to(cursor, goal);
5348 });
5349 });
5350 }
5351
5352 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
5353 if self.take_rename(true, cx).is_some() {
5354 return;
5355 }
5356
5357 if self
5358 .context_menu
5359 .write()
5360 .as_mut()
5361 .map(|menu| menu.select_last(self.project.as_ref(), cx))
5362 .unwrap_or(false)
5363 {
5364 return;
5365 }
5366
5367 if matches!(self.mode, EditorMode::SingleLine) {
5368 cx.propagate();
5369 return;
5370 }
5371
5372 let row_count = if let Some(row_count) = self.visible_line_count() {
5373 row_count as u32 - 1
5374 } else {
5375 return;
5376 };
5377
5378 let autoscroll = if action.center_cursor {
5379 Autoscroll::center()
5380 } else {
5381 Autoscroll::fit()
5382 };
5383
5384 let text_layout_details = &self.text_layout_details(cx);
5385 self.change_selections(Some(autoscroll), cx, |s| {
5386 let line_mode = s.line_mode;
5387 s.move_with(|map, selection| {
5388 if !selection.is_empty() && !line_mode {
5389 selection.goal = SelectionGoal::None;
5390 }
5391 let (cursor, goal) = movement::down_by_rows(
5392 map,
5393 selection.end,
5394 row_count,
5395 selection.goal,
5396 false,
5397 &text_layout_details,
5398 );
5399 selection.collapse_to(cursor, goal);
5400 });
5401 });
5402 }
5403
5404 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
5405 let text_layout_details = &self.text_layout_details(cx);
5406 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5407 s.move_heads_with(|map, head, goal| {
5408 movement::down(map, head, goal, false, &text_layout_details)
5409 })
5410 });
5411 }
5412
5413 pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
5414 if let Some(context_menu) = self.context_menu.write().as_mut() {
5415 context_menu.select_first(self.project.as_ref(), cx);
5416 }
5417 }
5418
5419 pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
5420 if let Some(context_menu) = self.context_menu.write().as_mut() {
5421 context_menu.select_prev(self.project.as_ref(), cx);
5422 }
5423 }
5424
5425 pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
5426 if let Some(context_menu) = self.context_menu.write().as_mut() {
5427 context_menu.select_next(self.project.as_ref(), cx);
5428 }
5429 }
5430
5431 pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
5432 if let Some(context_menu) = self.context_menu.write().as_mut() {
5433 context_menu.select_last(self.project.as_ref(), cx);
5434 }
5435 }
5436
5437 pub fn move_to_previous_word_start(
5438 &mut self,
5439 _: &MoveToPreviousWordStart,
5440 cx: &mut ViewContext<Self>,
5441 ) {
5442 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5443 s.move_cursors_with(|map, head, _| {
5444 (
5445 movement::previous_word_start(map, head),
5446 SelectionGoal::None,
5447 )
5448 });
5449 })
5450 }
5451
5452 pub fn move_to_previous_subword_start(
5453 &mut self,
5454 _: &MoveToPreviousSubwordStart,
5455 cx: &mut ViewContext<Self>,
5456 ) {
5457 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5458 s.move_cursors_with(|map, head, _| {
5459 (
5460 movement::previous_subword_start(map, head),
5461 SelectionGoal::None,
5462 )
5463 });
5464 })
5465 }
5466
5467 pub fn select_to_previous_word_start(
5468 &mut self,
5469 _: &SelectToPreviousWordStart,
5470 cx: &mut ViewContext<Self>,
5471 ) {
5472 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5473 s.move_heads_with(|map, head, _| {
5474 (
5475 movement::previous_word_start(map, head),
5476 SelectionGoal::None,
5477 )
5478 });
5479 })
5480 }
5481
5482 pub fn select_to_previous_subword_start(
5483 &mut self,
5484 _: &SelectToPreviousSubwordStart,
5485 cx: &mut ViewContext<Self>,
5486 ) {
5487 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5488 s.move_heads_with(|map, head, _| {
5489 (
5490 movement::previous_subword_start(map, head),
5491 SelectionGoal::None,
5492 )
5493 });
5494 })
5495 }
5496
5497 pub fn delete_to_previous_word_start(
5498 &mut self,
5499 _: &DeleteToPreviousWordStart,
5500 cx: &mut ViewContext<Self>,
5501 ) {
5502 self.transact(cx, |this, cx| {
5503 this.select_autoclose_pair(cx);
5504 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5505 let line_mode = s.line_mode;
5506 s.move_with(|map, selection| {
5507 if selection.is_empty() && !line_mode {
5508 let cursor = movement::previous_word_start(map, selection.head());
5509 selection.set_head(cursor, SelectionGoal::None);
5510 }
5511 });
5512 });
5513 this.insert("", cx);
5514 });
5515 }
5516
5517 pub fn delete_to_previous_subword_start(
5518 &mut self,
5519 _: &DeleteToPreviousSubwordStart,
5520 cx: &mut ViewContext<Self>,
5521 ) {
5522 self.transact(cx, |this, cx| {
5523 this.select_autoclose_pair(cx);
5524 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5525 let line_mode = s.line_mode;
5526 s.move_with(|map, selection| {
5527 if selection.is_empty() && !line_mode {
5528 let cursor = movement::previous_subword_start(map, selection.head());
5529 selection.set_head(cursor, SelectionGoal::None);
5530 }
5531 });
5532 });
5533 this.insert("", cx);
5534 });
5535 }
5536
5537 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
5538 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5539 s.move_cursors_with(|map, head, _| {
5540 (movement::next_word_end(map, head), SelectionGoal::None)
5541 });
5542 })
5543 }
5544
5545 pub fn move_to_next_subword_end(
5546 &mut self,
5547 _: &MoveToNextSubwordEnd,
5548 cx: &mut ViewContext<Self>,
5549 ) {
5550 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5551 s.move_cursors_with(|map, head, _| {
5552 (movement::next_subword_end(map, head), SelectionGoal::None)
5553 });
5554 })
5555 }
5556
5557 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
5558 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5559 s.move_heads_with(|map, head, _| {
5560 (movement::next_word_end(map, head), SelectionGoal::None)
5561 });
5562 })
5563 }
5564
5565 pub fn select_to_next_subword_end(
5566 &mut self,
5567 _: &SelectToNextSubwordEnd,
5568 cx: &mut ViewContext<Self>,
5569 ) {
5570 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5571 s.move_heads_with(|map, head, _| {
5572 (movement::next_subword_end(map, head), SelectionGoal::None)
5573 });
5574 })
5575 }
5576
5577 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
5578 self.transact(cx, |this, cx| {
5579 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5580 let line_mode = s.line_mode;
5581 s.move_with(|map, selection| {
5582 if selection.is_empty() && !line_mode {
5583 let cursor = movement::next_word_end(map, selection.head());
5584 selection.set_head(cursor, SelectionGoal::None);
5585 }
5586 });
5587 });
5588 this.insert("", cx);
5589 });
5590 }
5591
5592 pub fn delete_to_next_subword_end(
5593 &mut self,
5594 _: &DeleteToNextSubwordEnd,
5595 cx: &mut ViewContext<Self>,
5596 ) {
5597 self.transact(cx, |this, cx| {
5598 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5599 s.move_with(|map, selection| {
5600 if selection.is_empty() {
5601 let cursor = movement::next_subword_end(map, selection.head());
5602 selection.set_head(cursor, SelectionGoal::None);
5603 }
5604 });
5605 });
5606 this.insert("", cx);
5607 });
5608 }
5609
5610 pub fn move_to_beginning_of_line(
5611 &mut self,
5612 _: &MoveToBeginningOfLine,
5613 cx: &mut ViewContext<Self>,
5614 ) {
5615 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5616 s.move_cursors_with(|map, head, _| {
5617 (
5618 movement::indented_line_beginning(map, head, true),
5619 SelectionGoal::None,
5620 )
5621 });
5622 })
5623 }
5624
5625 pub fn select_to_beginning_of_line(
5626 &mut self,
5627 action: &SelectToBeginningOfLine,
5628 cx: &mut ViewContext<Self>,
5629 ) {
5630 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5631 s.move_heads_with(|map, head, _| {
5632 (
5633 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
5634 SelectionGoal::None,
5635 )
5636 });
5637 });
5638 }
5639
5640 pub fn delete_to_beginning_of_line(
5641 &mut self,
5642 _: &DeleteToBeginningOfLine,
5643 cx: &mut ViewContext<Self>,
5644 ) {
5645 self.transact(cx, |this, cx| {
5646 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5647 s.move_with(|_, selection| {
5648 selection.reversed = true;
5649 });
5650 });
5651
5652 this.select_to_beginning_of_line(
5653 &SelectToBeginningOfLine {
5654 stop_at_soft_wraps: false,
5655 },
5656 cx,
5657 );
5658 this.backspace(&Backspace, cx);
5659 });
5660 }
5661
5662 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
5663 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5664 s.move_cursors_with(|map, head, _| {
5665 (movement::line_end(map, head, true), SelectionGoal::None)
5666 });
5667 })
5668 }
5669
5670 pub fn select_to_end_of_line(
5671 &mut self,
5672 action: &SelectToEndOfLine,
5673 cx: &mut ViewContext<Self>,
5674 ) {
5675 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5676 s.move_heads_with(|map, head, _| {
5677 (
5678 movement::line_end(map, head, action.stop_at_soft_wraps),
5679 SelectionGoal::None,
5680 )
5681 });
5682 })
5683 }
5684
5685 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
5686 self.transact(cx, |this, cx| {
5687 this.select_to_end_of_line(
5688 &SelectToEndOfLine {
5689 stop_at_soft_wraps: false,
5690 },
5691 cx,
5692 );
5693 this.delete(&Delete, cx);
5694 });
5695 }
5696
5697 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
5698 self.transact(cx, |this, cx| {
5699 this.select_to_end_of_line(
5700 &SelectToEndOfLine {
5701 stop_at_soft_wraps: false,
5702 },
5703 cx,
5704 );
5705 this.cut(&Cut, cx);
5706 });
5707 }
5708
5709 pub fn move_to_start_of_paragraph(
5710 &mut self,
5711 _: &MoveToStartOfParagraph,
5712 cx: &mut ViewContext<Self>,
5713 ) {
5714 if matches!(self.mode, EditorMode::SingleLine) {
5715 cx.propagate();
5716 return;
5717 }
5718
5719 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5720 s.move_with(|map, selection| {
5721 selection.collapse_to(
5722 movement::start_of_paragraph(map, selection.head(), 1),
5723 SelectionGoal::None,
5724 )
5725 });
5726 })
5727 }
5728
5729 pub fn move_to_end_of_paragraph(
5730 &mut self,
5731 _: &MoveToEndOfParagraph,
5732 cx: &mut ViewContext<Self>,
5733 ) {
5734 if matches!(self.mode, EditorMode::SingleLine) {
5735 cx.propagate();
5736 return;
5737 }
5738
5739 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5740 s.move_with(|map, selection| {
5741 selection.collapse_to(
5742 movement::end_of_paragraph(map, selection.head(), 1),
5743 SelectionGoal::None,
5744 )
5745 });
5746 })
5747 }
5748
5749 pub fn select_to_start_of_paragraph(
5750 &mut self,
5751 _: &SelectToStartOfParagraph,
5752 cx: &mut ViewContext<Self>,
5753 ) {
5754 if matches!(self.mode, EditorMode::SingleLine) {
5755 cx.propagate();
5756 return;
5757 }
5758
5759 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5760 s.move_heads_with(|map, head, _| {
5761 (
5762 movement::start_of_paragraph(map, head, 1),
5763 SelectionGoal::None,
5764 )
5765 });
5766 })
5767 }
5768
5769 pub fn select_to_end_of_paragraph(
5770 &mut self,
5771 _: &SelectToEndOfParagraph,
5772 cx: &mut ViewContext<Self>,
5773 ) {
5774 if matches!(self.mode, EditorMode::SingleLine) {
5775 cx.propagate();
5776 return;
5777 }
5778
5779 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5780 s.move_heads_with(|map, head, _| {
5781 (
5782 movement::end_of_paragraph(map, head, 1),
5783 SelectionGoal::None,
5784 )
5785 });
5786 })
5787 }
5788
5789 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
5790 if matches!(self.mode, EditorMode::SingleLine) {
5791 cx.propagate();
5792 return;
5793 }
5794
5795 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5796 s.select_ranges(vec![0..0]);
5797 });
5798 }
5799
5800 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
5801 let mut selection = self.selections.last::<Point>(cx);
5802 selection.set_head(Point::zero(), SelectionGoal::None);
5803
5804 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5805 s.select(vec![selection]);
5806 });
5807 }
5808
5809 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
5810 if matches!(self.mode, EditorMode::SingleLine) {
5811 cx.propagate();
5812 return;
5813 }
5814
5815 let cursor = self.buffer.read(cx).read(cx).len();
5816 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5817 s.select_ranges(vec![cursor..cursor])
5818 });
5819 }
5820
5821 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
5822 self.nav_history = nav_history;
5823 }
5824
5825 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
5826 self.nav_history.as_ref()
5827 }
5828
5829 fn push_to_nav_history(
5830 &mut self,
5831 cursor_anchor: Anchor,
5832 new_position: Option<Point>,
5833 cx: &mut ViewContext<Self>,
5834 ) {
5835 if let Some(nav_history) = self.nav_history.as_mut() {
5836 let buffer = self.buffer.read(cx).read(cx);
5837 let cursor_position = cursor_anchor.to_point(&buffer);
5838 let scroll_state = self.scroll_manager.anchor();
5839 let scroll_top_row = scroll_state.top_row(&buffer);
5840 drop(buffer);
5841
5842 if let Some(new_position) = new_position {
5843 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
5844 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
5845 return;
5846 }
5847 }
5848
5849 nav_history.push(
5850 Some(NavigationData {
5851 cursor_anchor,
5852 cursor_position,
5853 scroll_anchor: scroll_state,
5854 scroll_top_row,
5855 }),
5856 cx,
5857 );
5858 }
5859 }
5860
5861 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
5862 let buffer = self.buffer.read(cx).snapshot(cx);
5863 let mut selection = self.selections.first::<usize>(cx);
5864 selection.set_head(buffer.len(), SelectionGoal::None);
5865 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5866 s.select(vec![selection]);
5867 });
5868 }
5869
5870 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
5871 let end = self.buffer.read(cx).read(cx).len();
5872 self.change_selections(None, cx, |s| {
5873 s.select_ranges(vec![0..end]);
5874 });
5875 }
5876
5877 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
5878 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5879 let mut selections = self.selections.all::<Point>(cx);
5880 let max_point = display_map.buffer_snapshot.max_point();
5881 for selection in &mut selections {
5882 let rows = selection.spanned_rows(true, &display_map);
5883 selection.start = Point::new(rows.start, 0);
5884 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
5885 selection.reversed = false;
5886 }
5887 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5888 s.select(selections);
5889 });
5890 }
5891
5892 pub fn split_selection_into_lines(
5893 &mut self,
5894 _: &SplitSelectionIntoLines,
5895 cx: &mut ViewContext<Self>,
5896 ) {
5897 let mut to_unfold = Vec::new();
5898 let mut new_selection_ranges = Vec::new();
5899 {
5900 let selections = self.selections.all::<Point>(cx);
5901 let buffer = self.buffer.read(cx).read(cx);
5902 for selection in selections {
5903 for row in selection.start.row..selection.end.row {
5904 let cursor = Point::new(row, buffer.line_len(row));
5905 new_selection_ranges.push(cursor..cursor);
5906 }
5907 new_selection_ranges.push(selection.end..selection.end);
5908 to_unfold.push(selection.start..selection.end);
5909 }
5910 }
5911 self.unfold_ranges(to_unfold, true, true, cx);
5912 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5913 s.select_ranges(new_selection_ranges);
5914 });
5915 }
5916
5917 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
5918 self.add_selection(true, cx);
5919 }
5920
5921 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
5922 self.add_selection(false, cx);
5923 }
5924
5925 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
5926 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5927 let mut selections = self.selections.all::<Point>(cx);
5928 let text_layout_details = self.text_layout_details(cx);
5929 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
5930 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
5931 let range = oldest_selection.display_range(&display_map).sorted();
5932
5933 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
5934 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
5935 let positions = start_x.min(end_x)..start_x.max(end_x);
5936
5937 selections.clear();
5938 let mut stack = Vec::new();
5939 for row in range.start.row()..=range.end.row() {
5940 if let Some(selection) = self.selections.build_columnar_selection(
5941 &display_map,
5942 row,
5943 &positions,
5944 oldest_selection.reversed,
5945 &text_layout_details,
5946 ) {
5947 stack.push(selection.id);
5948 selections.push(selection);
5949 }
5950 }
5951
5952 if above {
5953 stack.reverse();
5954 }
5955
5956 AddSelectionsState { above, stack }
5957 });
5958
5959 let last_added_selection = *state.stack.last().unwrap();
5960 let mut new_selections = Vec::new();
5961 if above == state.above {
5962 let end_row = if above {
5963 0
5964 } else {
5965 display_map.max_point().row()
5966 };
5967
5968 'outer: for selection in selections {
5969 if selection.id == last_added_selection {
5970 let range = selection.display_range(&display_map).sorted();
5971 debug_assert_eq!(range.start.row(), range.end.row());
5972 let mut row = range.start.row();
5973 let positions =
5974 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
5975 px(start)..px(end)
5976 } else {
5977 let start_x =
5978 display_map.x_for_display_point(range.start, &text_layout_details);
5979 let end_x =
5980 display_map.x_for_display_point(range.end, &text_layout_details);
5981 start_x.min(end_x)..start_x.max(end_x)
5982 };
5983
5984 while row != end_row {
5985 if above {
5986 row -= 1;
5987 } else {
5988 row += 1;
5989 }
5990
5991 if let Some(new_selection) = self.selections.build_columnar_selection(
5992 &display_map,
5993 row,
5994 &positions,
5995 selection.reversed,
5996 &text_layout_details,
5997 ) {
5998 state.stack.push(new_selection.id);
5999 if above {
6000 new_selections.push(new_selection);
6001 new_selections.push(selection);
6002 } else {
6003 new_selections.push(selection);
6004 new_selections.push(new_selection);
6005 }
6006
6007 continue 'outer;
6008 }
6009 }
6010 }
6011
6012 new_selections.push(selection);
6013 }
6014 } else {
6015 new_selections = selections;
6016 new_selections.retain(|s| s.id != last_added_selection);
6017 state.stack.pop();
6018 }
6019
6020 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6021 s.select(new_selections);
6022 });
6023 if state.stack.len() > 1 {
6024 self.add_selections_state = Some(state);
6025 }
6026 }
6027
6028 pub fn select_next_match_internal(
6029 &mut self,
6030 display_map: &DisplaySnapshot,
6031 replace_newest: bool,
6032 autoscroll: Option<Autoscroll>,
6033 cx: &mut ViewContext<Self>,
6034 ) -> Result<()> {
6035 fn select_next_match_ranges(
6036 this: &mut Editor,
6037 range: Range<usize>,
6038 replace_newest: bool,
6039 auto_scroll: Option<Autoscroll>,
6040 cx: &mut ViewContext<Editor>,
6041 ) {
6042 this.unfold_ranges([range.clone()], false, true, cx);
6043 this.change_selections(auto_scroll, cx, |s| {
6044 if replace_newest {
6045 s.delete(s.newest_anchor().id);
6046 }
6047 s.insert_range(range.clone());
6048 });
6049 }
6050
6051 let buffer = &display_map.buffer_snapshot;
6052 let mut selections = self.selections.all::<usize>(cx);
6053 if let Some(mut select_next_state) = self.select_next_state.take() {
6054 let query = &select_next_state.query;
6055 if !select_next_state.done {
6056 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
6057 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
6058 let mut next_selected_range = None;
6059
6060 let bytes_after_last_selection =
6061 buffer.bytes_in_range(last_selection.end..buffer.len());
6062 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
6063 let query_matches = query
6064 .stream_find_iter(bytes_after_last_selection)
6065 .map(|result| (last_selection.end, result))
6066 .chain(
6067 query
6068 .stream_find_iter(bytes_before_first_selection)
6069 .map(|result| (0, result)),
6070 );
6071
6072 for (start_offset, query_match) in query_matches {
6073 let query_match = query_match.unwrap(); // can only fail due to I/O
6074 let offset_range =
6075 start_offset + query_match.start()..start_offset + query_match.end();
6076 let display_range = offset_range.start.to_display_point(&display_map)
6077 ..offset_range.end.to_display_point(&display_map);
6078
6079 if !select_next_state.wordwise
6080 || (!movement::is_inside_word(&display_map, display_range.start)
6081 && !movement::is_inside_word(&display_map, display_range.end))
6082 {
6083 if selections
6084 .iter()
6085 .find(|selection| selection.range().overlaps(&offset_range))
6086 .is_none()
6087 {
6088 next_selected_range = Some(offset_range);
6089 break;
6090 }
6091 }
6092 }
6093
6094 if let Some(next_selected_range) = next_selected_range {
6095 select_next_match_ranges(
6096 self,
6097 next_selected_range,
6098 replace_newest,
6099 autoscroll,
6100 cx,
6101 );
6102 } else {
6103 select_next_state.done = true;
6104 }
6105 }
6106
6107 self.select_next_state = Some(select_next_state);
6108 } else {
6109 let mut only_carets = true;
6110 let mut same_text_selected = true;
6111 let mut selected_text = None;
6112
6113 let mut selections_iter = selections.iter().peekable();
6114 while let Some(selection) = selections_iter.next() {
6115 if selection.start != selection.end {
6116 only_carets = false;
6117 }
6118
6119 if same_text_selected {
6120 if selected_text.is_none() {
6121 selected_text =
6122 Some(buffer.text_for_range(selection.range()).collect::<String>());
6123 }
6124
6125 if let Some(next_selection) = selections_iter.peek() {
6126 if next_selection.range().len() == selection.range().len() {
6127 let next_selected_text = buffer
6128 .text_for_range(next_selection.range())
6129 .collect::<String>();
6130 if Some(next_selected_text) != selected_text {
6131 same_text_selected = false;
6132 selected_text = None;
6133 }
6134 } else {
6135 same_text_selected = false;
6136 selected_text = None;
6137 }
6138 }
6139 }
6140 }
6141
6142 if only_carets {
6143 for selection in &mut selections {
6144 let word_range = movement::surrounding_word(
6145 &display_map,
6146 selection.start.to_display_point(&display_map),
6147 );
6148 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
6149 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
6150 selection.goal = SelectionGoal::None;
6151 selection.reversed = false;
6152 select_next_match_ranges(
6153 self,
6154 selection.start..selection.end,
6155 replace_newest,
6156 autoscroll,
6157 cx,
6158 );
6159 }
6160
6161 if selections.len() == 1 {
6162 let selection = selections
6163 .last()
6164 .expect("ensured that there's only one selection");
6165 let query = buffer
6166 .text_for_range(selection.start..selection.end)
6167 .collect::<String>();
6168 let is_empty = query.is_empty();
6169 let select_state = SelectNextState {
6170 query: AhoCorasick::new(&[query])?,
6171 wordwise: true,
6172 done: is_empty,
6173 };
6174 self.select_next_state = Some(select_state);
6175 } else {
6176 self.select_next_state = None;
6177 }
6178 } else if let Some(selected_text) = selected_text {
6179 self.select_next_state = Some(SelectNextState {
6180 query: AhoCorasick::new(&[selected_text])?,
6181 wordwise: false,
6182 done: false,
6183 });
6184 self.select_next_match_internal(display_map, replace_newest, autoscroll, cx)?;
6185 }
6186 }
6187 Ok(())
6188 }
6189
6190 pub fn select_all_matches(
6191 &mut self,
6192 action: &SelectAllMatches,
6193 cx: &mut ViewContext<Self>,
6194 ) -> Result<()> {
6195 self.push_to_selection_history();
6196 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6197
6198 loop {
6199 self.select_next_match_internal(&display_map, action.replace_newest, None, cx)?;
6200
6201 if self
6202 .select_next_state
6203 .as_ref()
6204 .map(|selection_state| selection_state.done)
6205 .unwrap_or(true)
6206 {
6207 break;
6208 }
6209 }
6210
6211 Ok(())
6212 }
6213
6214 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) -> Result<()> {
6215 self.push_to_selection_history();
6216 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6217 self.select_next_match_internal(
6218 &display_map,
6219 action.replace_newest,
6220 Some(Autoscroll::newest()),
6221 cx,
6222 )?;
6223 Ok(())
6224 }
6225
6226 pub fn select_previous(
6227 &mut self,
6228 action: &SelectPrevious,
6229 cx: &mut ViewContext<Self>,
6230 ) -> Result<()> {
6231 self.push_to_selection_history();
6232 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6233 let buffer = &display_map.buffer_snapshot;
6234 let mut selections = self.selections.all::<usize>(cx);
6235 if let Some(mut select_prev_state) = self.select_prev_state.take() {
6236 let query = &select_prev_state.query;
6237 if !select_prev_state.done {
6238 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
6239 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
6240 let mut next_selected_range = None;
6241 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
6242 let bytes_before_last_selection =
6243 buffer.reversed_bytes_in_range(0..last_selection.start);
6244 let bytes_after_first_selection =
6245 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
6246 let query_matches = query
6247 .stream_find_iter(bytes_before_last_selection)
6248 .map(|result| (last_selection.start, result))
6249 .chain(
6250 query
6251 .stream_find_iter(bytes_after_first_selection)
6252 .map(|result| (buffer.len(), result)),
6253 );
6254 for (end_offset, query_match) in query_matches {
6255 let query_match = query_match.unwrap(); // can only fail due to I/O
6256 let offset_range =
6257 end_offset - query_match.end()..end_offset - query_match.start();
6258 let display_range = offset_range.start.to_display_point(&display_map)
6259 ..offset_range.end.to_display_point(&display_map);
6260
6261 if !select_prev_state.wordwise
6262 || (!movement::is_inside_word(&display_map, display_range.start)
6263 && !movement::is_inside_word(&display_map, display_range.end))
6264 {
6265 next_selected_range = Some(offset_range);
6266 break;
6267 }
6268 }
6269
6270 if let Some(next_selected_range) = next_selected_range {
6271 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
6272 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
6273 if action.replace_newest {
6274 s.delete(s.newest_anchor().id);
6275 }
6276 s.insert_range(next_selected_range);
6277 });
6278 } else {
6279 select_prev_state.done = true;
6280 }
6281 }
6282
6283 self.select_prev_state = Some(select_prev_state);
6284 } else {
6285 let mut only_carets = true;
6286 let mut same_text_selected = true;
6287 let mut selected_text = None;
6288
6289 let mut selections_iter = selections.iter().peekable();
6290 while let Some(selection) = selections_iter.next() {
6291 if selection.start != selection.end {
6292 only_carets = false;
6293 }
6294
6295 if same_text_selected {
6296 if selected_text.is_none() {
6297 selected_text =
6298 Some(buffer.text_for_range(selection.range()).collect::<String>());
6299 }
6300
6301 if let Some(next_selection) = selections_iter.peek() {
6302 if next_selection.range().len() == selection.range().len() {
6303 let next_selected_text = buffer
6304 .text_for_range(next_selection.range())
6305 .collect::<String>();
6306 if Some(next_selected_text) != selected_text {
6307 same_text_selected = false;
6308 selected_text = None;
6309 }
6310 } else {
6311 same_text_selected = false;
6312 selected_text = None;
6313 }
6314 }
6315 }
6316 }
6317
6318 if only_carets {
6319 for selection in &mut selections {
6320 let word_range = movement::surrounding_word(
6321 &display_map,
6322 selection.start.to_display_point(&display_map),
6323 );
6324 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
6325 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
6326 selection.goal = SelectionGoal::None;
6327 selection.reversed = false;
6328 }
6329 if selections.len() == 1 {
6330 let selection = selections
6331 .last()
6332 .expect("ensured that there's only one selection");
6333 let query = buffer
6334 .text_for_range(selection.start..selection.end)
6335 .collect::<String>();
6336 let is_empty = query.is_empty();
6337 let select_state = SelectNextState {
6338 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
6339 wordwise: true,
6340 done: is_empty,
6341 };
6342 self.select_prev_state = Some(select_state);
6343 } else {
6344 self.select_prev_state = None;
6345 }
6346
6347 self.unfold_ranges(
6348 selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
6349 false,
6350 true,
6351 cx,
6352 );
6353 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
6354 s.select(selections);
6355 });
6356 } else if let Some(selected_text) = selected_text {
6357 self.select_prev_state = Some(SelectNextState {
6358 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
6359 wordwise: false,
6360 done: false,
6361 });
6362 self.select_previous(action, cx)?;
6363 }
6364 }
6365 Ok(())
6366 }
6367
6368 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
6369 let text_layout_details = &self.text_layout_details(cx);
6370 self.transact(cx, |this, cx| {
6371 let mut selections = this.selections.all::<Point>(cx);
6372 let mut edits = Vec::new();
6373 let mut selection_edit_ranges = Vec::new();
6374 let mut last_toggled_row = None;
6375 let snapshot = this.buffer.read(cx).read(cx);
6376 let empty_str: Arc<str> = "".into();
6377 let mut suffixes_inserted = Vec::new();
6378
6379 fn comment_prefix_range(
6380 snapshot: &MultiBufferSnapshot,
6381 row: u32,
6382 comment_prefix: &str,
6383 comment_prefix_whitespace: &str,
6384 ) -> Range<Point> {
6385 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
6386
6387 let mut line_bytes = snapshot
6388 .bytes_in_range(start..snapshot.max_point())
6389 .flatten()
6390 .copied();
6391
6392 // If this line currently begins with the line comment prefix, then record
6393 // the range containing the prefix.
6394 if line_bytes
6395 .by_ref()
6396 .take(comment_prefix.len())
6397 .eq(comment_prefix.bytes())
6398 {
6399 // Include any whitespace that matches the comment prefix.
6400 let matching_whitespace_len = line_bytes
6401 .zip(comment_prefix_whitespace.bytes())
6402 .take_while(|(a, b)| a == b)
6403 .count() as u32;
6404 let end = Point::new(
6405 start.row,
6406 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
6407 );
6408 start..end
6409 } else {
6410 start..start
6411 }
6412 }
6413
6414 fn comment_suffix_range(
6415 snapshot: &MultiBufferSnapshot,
6416 row: u32,
6417 comment_suffix: &str,
6418 comment_suffix_has_leading_space: bool,
6419 ) -> Range<Point> {
6420 let end = Point::new(row, snapshot.line_len(row));
6421 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
6422
6423 let mut line_end_bytes = snapshot
6424 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
6425 .flatten()
6426 .copied();
6427
6428 let leading_space_len = if suffix_start_column > 0
6429 && line_end_bytes.next() == Some(b' ')
6430 && comment_suffix_has_leading_space
6431 {
6432 1
6433 } else {
6434 0
6435 };
6436
6437 // If this line currently begins with the line comment prefix, then record
6438 // the range containing the prefix.
6439 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
6440 let start = Point::new(end.row, suffix_start_column - leading_space_len);
6441 start..end
6442 } else {
6443 end..end
6444 }
6445 }
6446
6447 // TODO: Handle selections that cross excerpts
6448 for selection in &mut selections {
6449 let start_column = snapshot.indent_size_for_line(selection.start.row).len;
6450 let language = if let Some(language) =
6451 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
6452 {
6453 language
6454 } else {
6455 continue;
6456 };
6457
6458 selection_edit_ranges.clear();
6459
6460 // If multiple selections contain a given row, avoid processing that
6461 // row more than once.
6462 let mut start_row = selection.start.row;
6463 if last_toggled_row == Some(start_row) {
6464 start_row += 1;
6465 }
6466 let end_row =
6467 if selection.end.row > selection.start.row && selection.end.column == 0 {
6468 selection.end.row - 1
6469 } else {
6470 selection.end.row
6471 };
6472 last_toggled_row = Some(end_row);
6473
6474 if start_row > end_row {
6475 continue;
6476 }
6477
6478 // If the language has line comments, toggle those.
6479 if let Some(full_comment_prefix) = language.line_comment_prefix() {
6480 // Split the comment prefix's trailing whitespace into a separate string,
6481 // as that portion won't be used for detecting if a line is a comment.
6482 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
6483 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
6484 let mut all_selection_lines_are_comments = true;
6485
6486 for row in start_row..=end_row {
6487 if snapshot.is_line_blank(row) && start_row < end_row {
6488 continue;
6489 }
6490
6491 let prefix_range = comment_prefix_range(
6492 snapshot.deref(),
6493 row,
6494 comment_prefix,
6495 comment_prefix_whitespace,
6496 );
6497 if prefix_range.is_empty() {
6498 all_selection_lines_are_comments = false;
6499 }
6500 selection_edit_ranges.push(prefix_range);
6501 }
6502
6503 if all_selection_lines_are_comments {
6504 edits.extend(
6505 selection_edit_ranges
6506 .iter()
6507 .cloned()
6508 .map(|range| (range, empty_str.clone())),
6509 );
6510 } else {
6511 let min_column = selection_edit_ranges
6512 .iter()
6513 .map(|r| r.start.column)
6514 .min()
6515 .unwrap_or(0);
6516 edits.extend(selection_edit_ranges.iter().map(|range| {
6517 let position = Point::new(range.start.row, min_column);
6518 (position..position, full_comment_prefix.clone())
6519 }));
6520 }
6521 } else if let Some((full_comment_prefix, comment_suffix)) =
6522 language.block_comment_delimiters()
6523 {
6524 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
6525 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
6526 let prefix_range = comment_prefix_range(
6527 snapshot.deref(),
6528 start_row,
6529 comment_prefix,
6530 comment_prefix_whitespace,
6531 );
6532 let suffix_range = comment_suffix_range(
6533 snapshot.deref(),
6534 end_row,
6535 comment_suffix.trim_start_matches(' '),
6536 comment_suffix.starts_with(' '),
6537 );
6538
6539 if prefix_range.is_empty() || suffix_range.is_empty() {
6540 edits.push((
6541 prefix_range.start..prefix_range.start,
6542 full_comment_prefix.clone(),
6543 ));
6544 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
6545 suffixes_inserted.push((end_row, comment_suffix.len()));
6546 } else {
6547 edits.push((prefix_range, empty_str.clone()));
6548 edits.push((suffix_range, empty_str.clone()));
6549 }
6550 } else {
6551 continue;
6552 }
6553 }
6554
6555 drop(snapshot);
6556 this.buffer.update(cx, |buffer, cx| {
6557 buffer.edit(edits, None, cx);
6558 });
6559
6560 // Adjust selections so that they end before any comment suffixes that
6561 // were inserted.
6562 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
6563 let mut selections = this.selections.all::<Point>(cx);
6564 let snapshot = this.buffer.read(cx).read(cx);
6565 for selection in &mut selections {
6566 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
6567 match row.cmp(&selection.end.row) {
6568 Ordering::Less => {
6569 suffixes_inserted.next();
6570 continue;
6571 }
6572 Ordering::Greater => break,
6573 Ordering::Equal => {
6574 if selection.end.column == snapshot.line_len(row) {
6575 if selection.is_empty() {
6576 selection.start.column -= suffix_len as u32;
6577 }
6578 selection.end.column -= suffix_len as u32;
6579 }
6580 break;
6581 }
6582 }
6583 }
6584 }
6585
6586 drop(snapshot);
6587 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6588
6589 let selections = this.selections.all::<Point>(cx);
6590 let selections_on_single_row = selections.windows(2).all(|selections| {
6591 selections[0].start.row == selections[1].start.row
6592 && selections[0].end.row == selections[1].end.row
6593 && selections[0].start.row == selections[0].end.row
6594 });
6595 let selections_selecting = selections
6596 .iter()
6597 .any(|selection| selection.start != selection.end);
6598 let advance_downwards = action.advance_downwards
6599 && selections_on_single_row
6600 && !selections_selecting
6601 && this.mode != EditorMode::SingleLine;
6602
6603 if advance_downwards {
6604 let snapshot = this.buffer.read(cx).snapshot(cx);
6605
6606 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6607 s.move_cursors_with(|display_snapshot, display_point, _| {
6608 let mut point = display_point.to_point(display_snapshot);
6609 point.row += 1;
6610 point = snapshot.clip_point(point, Bias::Left);
6611 let display_point = point.to_display_point(display_snapshot);
6612 let goal = SelectionGoal::HorizontalPosition(
6613 display_snapshot
6614 .x_for_display_point(display_point, &text_layout_details)
6615 .into(),
6616 );
6617 (display_point, goal)
6618 })
6619 });
6620 }
6621 });
6622 }
6623
6624 pub fn select_larger_syntax_node(
6625 &mut self,
6626 _: &SelectLargerSyntaxNode,
6627 cx: &mut ViewContext<Self>,
6628 ) {
6629 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6630 let buffer = self.buffer.read(cx).snapshot(cx);
6631 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
6632
6633 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
6634 let mut selected_larger_node = false;
6635 let new_selections = old_selections
6636 .iter()
6637 .map(|selection| {
6638 let old_range = selection.start..selection.end;
6639 let mut new_range = old_range.clone();
6640 while let Some(containing_range) =
6641 buffer.range_for_syntax_ancestor(new_range.clone())
6642 {
6643 new_range = containing_range;
6644 if !display_map.intersects_fold(new_range.start)
6645 && !display_map.intersects_fold(new_range.end)
6646 {
6647 break;
6648 }
6649 }
6650
6651 selected_larger_node |= new_range != old_range;
6652 Selection {
6653 id: selection.id,
6654 start: new_range.start,
6655 end: new_range.end,
6656 goal: SelectionGoal::None,
6657 reversed: selection.reversed,
6658 }
6659 })
6660 .collect::<Vec<_>>();
6661
6662 if selected_larger_node {
6663 stack.push(old_selections);
6664 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6665 s.select(new_selections);
6666 });
6667 }
6668 self.select_larger_syntax_node_stack = stack;
6669 }
6670
6671 pub fn select_smaller_syntax_node(
6672 &mut self,
6673 _: &SelectSmallerSyntaxNode,
6674 cx: &mut ViewContext<Self>,
6675 ) {
6676 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
6677 if let Some(selections) = stack.pop() {
6678 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6679 s.select(selections.to_vec());
6680 });
6681 }
6682 self.select_larger_syntax_node_stack = stack;
6683 }
6684
6685 pub fn move_to_enclosing_bracket(
6686 &mut self,
6687 _: &MoveToEnclosingBracket,
6688 cx: &mut ViewContext<Self>,
6689 ) {
6690 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6691 s.move_offsets_with(|snapshot, selection| {
6692 let Some(enclosing_bracket_ranges) =
6693 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
6694 else {
6695 return;
6696 };
6697
6698 let mut best_length = usize::MAX;
6699 let mut best_inside = false;
6700 let mut best_in_bracket_range = false;
6701 let mut best_destination = None;
6702 for (open, close) in enclosing_bracket_ranges {
6703 let close = close.to_inclusive();
6704 let length = close.end() - open.start;
6705 let inside = selection.start >= open.end && selection.end <= *close.start();
6706 let in_bracket_range = open.to_inclusive().contains(&selection.head())
6707 || close.contains(&selection.head());
6708
6709 // If best is next to a bracket and current isn't, skip
6710 if !in_bracket_range && best_in_bracket_range {
6711 continue;
6712 }
6713
6714 // Prefer smaller lengths unless best is inside and current isn't
6715 if length > best_length && (best_inside || !inside) {
6716 continue;
6717 }
6718
6719 best_length = length;
6720 best_inside = inside;
6721 best_in_bracket_range = in_bracket_range;
6722 best_destination = Some(
6723 if close.contains(&selection.start) && close.contains(&selection.end) {
6724 if inside {
6725 open.end
6726 } else {
6727 open.start
6728 }
6729 } else {
6730 if inside {
6731 *close.start()
6732 } else {
6733 *close.end()
6734 }
6735 },
6736 );
6737 }
6738
6739 if let Some(destination) = best_destination {
6740 selection.collapse_to(destination, SelectionGoal::None);
6741 }
6742 })
6743 });
6744 }
6745
6746 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
6747 self.end_selection(cx);
6748 self.selection_history.mode = SelectionHistoryMode::Undoing;
6749 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
6750 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
6751 self.select_next_state = entry.select_next_state;
6752 self.select_prev_state = entry.select_prev_state;
6753 self.add_selections_state = entry.add_selections_state;
6754 self.request_autoscroll(Autoscroll::newest(), cx);
6755 }
6756 self.selection_history.mode = SelectionHistoryMode::Normal;
6757 }
6758
6759 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
6760 self.end_selection(cx);
6761 self.selection_history.mode = SelectionHistoryMode::Redoing;
6762 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
6763 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
6764 self.select_next_state = entry.select_next_state;
6765 self.select_prev_state = entry.select_prev_state;
6766 self.add_selections_state = entry.add_selections_state;
6767 self.request_autoscroll(Autoscroll::newest(), cx);
6768 }
6769 self.selection_history.mode = SelectionHistoryMode::Normal;
6770 }
6771
6772 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
6773 self.go_to_diagnostic_impl(Direction::Next, cx)
6774 }
6775
6776 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
6777 self.go_to_diagnostic_impl(Direction::Prev, cx)
6778 }
6779
6780 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
6781 let buffer = self.buffer.read(cx).snapshot(cx);
6782 let selection = self.selections.newest::<usize>(cx);
6783
6784 // If there is an active Diagnostic Popover jump to its diagnostic instead.
6785 if direction == Direction::Next {
6786 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
6787 let (group_id, jump_to) = popover.activation_info();
6788 if self.activate_diagnostics(group_id, cx) {
6789 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6790 let mut new_selection = s.newest_anchor().clone();
6791 new_selection.collapse_to(jump_to, SelectionGoal::None);
6792 s.select_anchors(vec![new_selection.clone()]);
6793 });
6794 }
6795 return;
6796 }
6797 }
6798
6799 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
6800 active_diagnostics
6801 .primary_range
6802 .to_offset(&buffer)
6803 .to_inclusive()
6804 });
6805 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
6806 if active_primary_range.contains(&selection.head()) {
6807 *active_primary_range.end()
6808 } else {
6809 selection.head()
6810 }
6811 } else {
6812 selection.head()
6813 };
6814
6815 loop {
6816 let mut diagnostics = if direction == Direction::Prev {
6817 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
6818 } else {
6819 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
6820 };
6821 let group = diagnostics.find_map(|entry| {
6822 if entry.diagnostic.is_primary
6823 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
6824 && !entry.range.is_empty()
6825 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
6826 && !entry.range.contains(&search_start)
6827 {
6828 Some((entry.range, entry.diagnostic.group_id))
6829 } else {
6830 None
6831 }
6832 });
6833
6834 if let Some((primary_range, group_id)) = group {
6835 if self.activate_diagnostics(group_id, cx) {
6836 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6837 s.select(vec![Selection {
6838 id: selection.id,
6839 start: primary_range.start,
6840 end: primary_range.start,
6841 reversed: false,
6842 goal: SelectionGoal::None,
6843 }]);
6844 });
6845 }
6846 break;
6847 } else {
6848 // Cycle around to the start of the buffer, potentially moving back to the start of
6849 // the currently active diagnostic.
6850 active_primary_range.take();
6851 if direction == Direction::Prev {
6852 if search_start == buffer.len() {
6853 break;
6854 } else {
6855 search_start = buffer.len();
6856 }
6857 } else if search_start == 0 {
6858 break;
6859 } else {
6860 search_start = 0;
6861 }
6862 }
6863 }
6864 }
6865
6866 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
6867 let snapshot = self
6868 .display_map
6869 .update(cx, |display_map, cx| display_map.snapshot(cx));
6870 let selection = self.selections.newest::<Point>(cx);
6871
6872 if !self.seek_in_direction(
6873 &snapshot,
6874 selection.head(),
6875 false,
6876 snapshot
6877 .buffer_snapshot
6878 .git_diff_hunks_in_range((selection.head().row + 1)..u32::MAX),
6879 cx,
6880 ) {
6881 let wrapped_point = Point::zero();
6882 self.seek_in_direction(
6883 &snapshot,
6884 wrapped_point,
6885 true,
6886 snapshot
6887 .buffer_snapshot
6888 .git_diff_hunks_in_range((wrapped_point.row + 1)..u32::MAX),
6889 cx,
6890 );
6891 }
6892 }
6893
6894 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
6895 let snapshot = self
6896 .display_map
6897 .update(cx, |display_map, cx| display_map.snapshot(cx));
6898 let selection = self.selections.newest::<Point>(cx);
6899
6900 if !self.seek_in_direction(
6901 &snapshot,
6902 selection.head(),
6903 false,
6904 snapshot
6905 .buffer_snapshot
6906 .git_diff_hunks_in_range_rev(0..selection.head().row),
6907 cx,
6908 ) {
6909 let wrapped_point = snapshot.buffer_snapshot.max_point();
6910 self.seek_in_direction(
6911 &snapshot,
6912 wrapped_point,
6913 true,
6914 snapshot
6915 .buffer_snapshot
6916 .git_diff_hunks_in_range_rev(0..wrapped_point.row),
6917 cx,
6918 );
6919 }
6920 }
6921
6922 fn seek_in_direction(
6923 &mut self,
6924 snapshot: &DisplaySnapshot,
6925 initial_point: Point,
6926 is_wrapped: bool,
6927 hunks: impl Iterator<Item = DiffHunk<u32>>,
6928 cx: &mut ViewContext<Editor>,
6929 ) -> bool {
6930 let display_point = initial_point.to_display_point(snapshot);
6931 let mut hunks = hunks
6932 .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
6933 .filter(|hunk| {
6934 if is_wrapped {
6935 true
6936 } else {
6937 !hunk.contains_display_row(display_point.row())
6938 }
6939 })
6940 .dedup();
6941
6942 if let Some(hunk) = hunks.next() {
6943 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6944 let row = hunk.start_display_row();
6945 let point = DisplayPoint::new(row, 0);
6946 s.select_display_ranges([point..point]);
6947 });
6948
6949 true
6950 } else {
6951 false
6952 }
6953 }
6954
6955 pub fn go_to_definition(&mut self, _: &GoToDefinition, cx: &mut ViewContext<Self>) {
6956 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
6957 }
6958
6959 pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext<Self>) {
6960 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx);
6961 }
6962
6963 pub fn go_to_definition_split(&mut self, _: &GoToDefinitionSplit, cx: &mut ViewContext<Self>) {
6964 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx);
6965 }
6966
6967 pub fn go_to_type_definition_split(
6968 &mut self,
6969 _: &GoToTypeDefinitionSplit,
6970 cx: &mut ViewContext<Self>,
6971 ) {
6972 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx);
6973 }
6974
6975 fn go_to_definition_of_kind(
6976 &mut self,
6977 kind: GotoDefinitionKind,
6978 split: bool,
6979 cx: &mut ViewContext<Self>,
6980 ) {
6981 let Some(workspace) = self.workspace() else {
6982 return;
6983 };
6984 let buffer = self.buffer.read(cx);
6985 let head = self.selections.newest::<usize>(cx).head();
6986 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
6987 text_anchor
6988 } else {
6989 return;
6990 };
6991
6992 let project = workspace.read(cx).project().clone();
6993 let definitions = project.update(cx, |project, cx| match kind {
6994 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
6995 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
6996 });
6997
6998 cx.spawn(|editor, mut cx| async move {
6999 let definitions = definitions.await?;
7000 editor.update(&mut cx, |editor, cx| {
7001 editor.navigate_to_definitions(
7002 definitions
7003 .into_iter()
7004 .map(GoToDefinitionLink::Text)
7005 .collect(),
7006 split,
7007 cx,
7008 );
7009 })?;
7010 Ok::<(), anyhow::Error>(())
7011 })
7012 .detach_and_log_err(cx);
7013 }
7014
7015 pub fn navigate_to_definitions(
7016 &mut self,
7017 mut definitions: Vec<GoToDefinitionLink>,
7018 split: bool,
7019 cx: &mut ViewContext<Editor>,
7020 ) {
7021 let Some(workspace) = self.workspace() else {
7022 return;
7023 };
7024 let pane = workspace.read(cx).active_pane().clone();
7025 // If there is one definition, just open it directly
7026 if definitions.len() == 1 {
7027 let definition = definitions.pop().unwrap();
7028 let target_task = match definition {
7029 GoToDefinitionLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
7030 GoToDefinitionLink::InlayHint(lsp_location, server_id) => {
7031 self.compute_target_location(lsp_location, server_id, cx)
7032 }
7033 };
7034 cx.spawn(|editor, mut cx| async move {
7035 let target = target_task.await.context("target resolution task")?;
7036 if let Some(target) = target {
7037 editor.update(&mut cx, |editor, cx| {
7038 let range = target.range.to_offset(target.buffer.read(cx));
7039 let range = editor.range_for_match(&range);
7040 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
7041 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
7042 s.select_ranges([range]);
7043 });
7044 } else {
7045 cx.window_context().defer(move |cx| {
7046 let target_editor: View<Self> =
7047 workspace.update(cx, |workspace, cx| {
7048 if split {
7049 workspace.split_project_item(target.buffer.clone(), cx)
7050 } else {
7051 workspace.open_project_item(target.buffer.clone(), cx)
7052 }
7053 });
7054 target_editor.update(cx, |target_editor, cx| {
7055 // When selecting a definition in a different buffer, disable the nav history
7056 // to avoid creating a history entry at the previous cursor location.
7057 pane.update(cx, |pane, _| pane.disable_history());
7058 target_editor.change_selections(
7059 Some(Autoscroll::fit()),
7060 cx,
7061 |s| {
7062 s.select_ranges([range]);
7063 },
7064 );
7065 pane.update(cx, |pane, _| pane.enable_history());
7066 });
7067 });
7068 }
7069 })
7070 } else {
7071 Ok(())
7072 }
7073 })
7074 .detach_and_log_err(cx);
7075 } else if !definitions.is_empty() {
7076 let replica_id = self.replica_id(cx);
7077 cx.spawn(|editor, mut cx| async move {
7078 let (title, location_tasks) = editor
7079 .update(&mut cx, |editor, cx| {
7080 let title = definitions
7081 .iter()
7082 .find_map(|definition| match definition {
7083 GoToDefinitionLink::Text(link) => {
7084 link.origin.as_ref().map(|origin| {
7085 let buffer = origin.buffer.read(cx);
7086 format!(
7087 "Definitions for {}",
7088 buffer
7089 .text_for_range(origin.range.clone())
7090 .collect::<String>()
7091 )
7092 })
7093 }
7094 GoToDefinitionLink::InlayHint(_, _) => None,
7095 })
7096 .unwrap_or("Definitions".to_string());
7097 let location_tasks = definitions
7098 .into_iter()
7099 .map(|definition| match definition {
7100 GoToDefinitionLink::Text(link) => {
7101 Task::Ready(Some(Ok(Some(link.target))))
7102 }
7103 GoToDefinitionLink::InlayHint(lsp_location, server_id) => {
7104 editor.compute_target_location(lsp_location, server_id, cx)
7105 }
7106 })
7107 .collect::<Vec<_>>();
7108 (title, location_tasks)
7109 })
7110 .context("location tasks preparation")?;
7111
7112 let locations = futures::future::join_all(location_tasks)
7113 .await
7114 .into_iter()
7115 .filter_map(|location| location.transpose())
7116 .collect::<Result<_>>()
7117 .context("location tasks")?;
7118 workspace
7119 .update(&mut cx, |workspace, cx| {
7120 Self::open_locations_in_multibuffer(
7121 workspace, locations, replica_id, title, split, cx,
7122 )
7123 })
7124 .ok();
7125
7126 anyhow::Ok(())
7127 })
7128 .detach_and_log_err(cx);
7129 }
7130 }
7131
7132 fn compute_target_location(
7133 &self,
7134 lsp_location: lsp::Location,
7135 server_id: LanguageServerId,
7136 cx: &mut ViewContext<Editor>,
7137 ) -> Task<anyhow::Result<Option<Location>>> {
7138 let Some(project) = self.project.clone() else {
7139 return Task::Ready(Some(Ok(None)));
7140 };
7141
7142 cx.spawn(move |editor, mut cx| async move {
7143 let location_task = editor.update(&mut cx, |editor, cx| {
7144 project.update(cx, |project, cx| {
7145 let language_server_name =
7146 editor.buffer.read(cx).as_singleton().and_then(|buffer| {
7147 project
7148 .language_server_for_buffer(buffer.read(cx), server_id, cx)
7149 .map(|(_, lsp_adapter)| {
7150 LanguageServerName(Arc::from(lsp_adapter.name()))
7151 })
7152 });
7153 language_server_name.map(|language_server_name| {
7154 project.open_local_buffer_via_lsp(
7155 lsp_location.uri.clone(),
7156 server_id,
7157 language_server_name,
7158 cx,
7159 )
7160 })
7161 })
7162 })?;
7163 let location = match location_task {
7164 Some(task) => Some({
7165 let target_buffer_handle = task.await.context("open local buffer")?;
7166 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
7167 let target_start = target_buffer
7168 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
7169 let target_end = target_buffer
7170 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
7171 target_buffer.anchor_after(target_start)
7172 ..target_buffer.anchor_before(target_end)
7173 })?;
7174 Location {
7175 buffer: target_buffer_handle,
7176 range,
7177 }
7178 }),
7179 None => None,
7180 };
7181 Ok(location)
7182 })
7183 }
7184
7185 pub fn find_all_references(
7186 &mut self,
7187 _: &FindAllReferences,
7188 cx: &mut ViewContext<Self>,
7189 ) -> Option<Task<Result<()>>> {
7190 let buffer = self.buffer.read(cx);
7191 let head = self.selections.newest::<usize>(cx).head();
7192 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
7193 let replica_id = self.replica_id(cx);
7194
7195 let workspace = self.workspace()?;
7196 let project = workspace.read(cx).project().clone();
7197 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
7198 Some(cx.spawn(|_, mut cx| async move {
7199 let locations = references.await?;
7200 if locations.is_empty() {
7201 return Ok(());
7202 }
7203
7204 workspace.update(&mut cx, |workspace, cx| {
7205 let title = locations
7206 .first()
7207 .as_ref()
7208 .map(|location| {
7209 let buffer = location.buffer.read(cx);
7210 format!(
7211 "References to `{}`",
7212 buffer
7213 .text_for_range(location.range.clone())
7214 .collect::<String>()
7215 )
7216 })
7217 .unwrap();
7218 Self::open_locations_in_multibuffer(
7219 workspace, locations, replica_id, title, false, cx,
7220 );
7221 })?;
7222
7223 Ok(())
7224 }))
7225 }
7226
7227 /// Opens a multibuffer with the given project locations in it
7228 pub fn open_locations_in_multibuffer(
7229 workspace: &mut Workspace,
7230 mut locations: Vec<Location>,
7231 replica_id: ReplicaId,
7232 title: String,
7233 split: bool,
7234 cx: &mut ViewContext<Workspace>,
7235 ) {
7236 // If there are multiple definitions, open them in a multibuffer
7237 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
7238 let mut locations = locations.into_iter().peekable();
7239 let mut ranges_to_highlight = Vec::new();
7240 let capability = workspace.project().read(cx).capability();
7241
7242 let excerpt_buffer = cx.new_model(|cx| {
7243 let mut multibuffer = MultiBuffer::new(replica_id, capability);
7244 while let Some(location) = locations.next() {
7245 let buffer = location.buffer.read(cx);
7246 let mut ranges_for_buffer = Vec::new();
7247 let range = location.range.to_offset(buffer);
7248 ranges_for_buffer.push(range.clone());
7249
7250 while let Some(next_location) = locations.peek() {
7251 if next_location.buffer == location.buffer {
7252 ranges_for_buffer.push(next_location.range.to_offset(buffer));
7253 locations.next();
7254 } else {
7255 break;
7256 }
7257 }
7258
7259 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
7260 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
7261 location.buffer.clone(),
7262 ranges_for_buffer,
7263 1,
7264 cx,
7265 ))
7266 }
7267
7268 multibuffer.with_title(title)
7269 });
7270
7271 let editor = cx.new_view(|cx| {
7272 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
7273 });
7274 editor.update(cx, |editor, cx| {
7275 editor.highlight_background::<Self>(
7276 ranges_to_highlight,
7277 |theme| theme.editor_highlighted_line_background,
7278 cx,
7279 );
7280 });
7281 if split {
7282 workspace.split_item(SplitDirection::Right, Box::new(editor), cx);
7283 } else {
7284 workspace.add_item(Box::new(editor), cx);
7285 }
7286 }
7287
7288 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
7289 use language::ToOffset as _;
7290
7291 let project = self.project.clone()?;
7292 let selection = self.selections.newest_anchor().clone();
7293 let (cursor_buffer, cursor_buffer_position) = self
7294 .buffer
7295 .read(cx)
7296 .text_anchor_for_position(selection.head(), cx)?;
7297 let (tail_buffer, _) = self
7298 .buffer
7299 .read(cx)
7300 .text_anchor_for_position(selection.tail(), cx)?;
7301 if tail_buffer != cursor_buffer {
7302 return None;
7303 }
7304
7305 let snapshot = cursor_buffer.read(cx).snapshot();
7306 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
7307 let prepare_rename = project.update(cx, |project, cx| {
7308 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
7309 });
7310
7311 Some(cx.spawn(|this, mut cx| async move {
7312 let rename_range = if let Some(range) = prepare_rename.await? {
7313 Some(range)
7314 } else {
7315 this.update(&mut cx, |this, cx| {
7316 let buffer = this.buffer.read(cx).snapshot(cx);
7317 let mut buffer_highlights = this
7318 .document_highlights_for_position(selection.head(), &buffer)
7319 .filter(|highlight| {
7320 highlight.start.excerpt_id == selection.head().excerpt_id
7321 && highlight.end.excerpt_id == selection.head().excerpt_id
7322 });
7323 buffer_highlights
7324 .next()
7325 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
7326 })?
7327 };
7328 if let Some(rename_range) = rename_range {
7329 let rename_buffer_range = rename_range.to_offset(&snapshot);
7330 let cursor_offset_in_rename_range =
7331 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
7332
7333 this.update(&mut cx, |this, cx| {
7334 this.take_rename(false, cx);
7335 let buffer = this.buffer.read(cx).read(cx);
7336 let cursor_offset = selection.head().to_offset(&buffer);
7337 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
7338 let rename_end = rename_start + rename_buffer_range.len();
7339 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
7340 let mut old_highlight_id = None;
7341 let old_name: Arc<str> = buffer
7342 .chunks(rename_start..rename_end, true)
7343 .map(|chunk| {
7344 if old_highlight_id.is_none() {
7345 old_highlight_id = chunk.syntax_highlight_id;
7346 }
7347 chunk.text
7348 })
7349 .collect::<String>()
7350 .into();
7351
7352 drop(buffer);
7353
7354 // Position the selection in the rename editor so that it matches the current selection.
7355 this.show_local_selections = false;
7356 let rename_editor = cx.new_view(|cx| {
7357 let mut editor = Editor::single_line(cx);
7358 editor.buffer.update(cx, |buffer, cx| {
7359 buffer.edit([(0..0, old_name.clone())], None, cx)
7360 });
7361 editor.select_all(&SelectAll, cx);
7362 editor
7363 });
7364
7365 let ranges = this
7366 .clear_background_highlights::<DocumentHighlightWrite>(cx)
7367 .into_iter()
7368 .flat_map(|(_, ranges)| ranges.into_iter())
7369 .chain(
7370 this.clear_background_highlights::<DocumentHighlightRead>(cx)
7371 .into_iter()
7372 .flat_map(|(_, ranges)| ranges.into_iter()),
7373 )
7374 .collect();
7375
7376 this.highlight_text::<Rename>(
7377 ranges,
7378 HighlightStyle {
7379 fade_out: Some(0.6),
7380 ..Default::default()
7381 },
7382 cx,
7383 );
7384 let rename_focus_handle = rename_editor.focus_handle(cx);
7385 cx.focus(&rename_focus_handle);
7386 let block_id = this.insert_blocks(
7387 [BlockProperties {
7388 style: BlockStyle::Flex,
7389 position: range.start.clone(),
7390 height: 1,
7391 render: Arc::new({
7392 let rename_editor = rename_editor.clone();
7393 move |cx: &mut BlockContext| {
7394 let mut text_style = cx.editor_style.text.clone();
7395 if let Some(highlight_style) = old_highlight_id
7396 .and_then(|h| h.style(&cx.editor_style.syntax))
7397 {
7398 text_style = text_style.highlight(highlight_style);
7399 }
7400 div()
7401 .pl(cx.anchor_x)
7402 .child(EditorElement::new(
7403 &rename_editor,
7404 EditorStyle {
7405 background: cx.theme().system().transparent,
7406 local_player: cx.editor_style.local_player,
7407 text: text_style,
7408 scrollbar_width: cx.editor_style.scrollbar_width,
7409 syntax: cx.editor_style.syntax.clone(),
7410 status: cx.editor_style.status.clone(),
7411 inlays_style: HighlightStyle {
7412 color: Some(cx.theme().status().hint),
7413 font_weight: Some(FontWeight::BOLD),
7414 ..HighlightStyle::default()
7415 },
7416 suggestions_style: HighlightStyle {
7417 color: Some(cx.theme().status().predictive),
7418 ..HighlightStyle::default()
7419 },
7420 },
7421 ))
7422 .into_any_element()
7423 }
7424 }),
7425 disposition: BlockDisposition::Below,
7426 }],
7427 Some(Autoscroll::fit()),
7428 cx,
7429 )[0];
7430 this.pending_rename = Some(RenameState {
7431 range,
7432 old_name,
7433 editor: rename_editor,
7434 block_id,
7435 });
7436 })?;
7437 }
7438
7439 Ok(())
7440 }))
7441 }
7442
7443 pub fn confirm_rename(
7444 &mut self,
7445 _: &ConfirmRename,
7446 cx: &mut ViewContext<Self>,
7447 ) -> Option<Task<Result<()>>> {
7448 let rename = self.take_rename(false, cx)?;
7449 let workspace = self.workspace()?;
7450 let (start_buffer, start) = self
7451 .buffer
7452 .read(cx)
7453 .text_anchor_for_position(rename.range.start.clone(), cx)?;
7454 let (end_buffer, end) = self
7455 .buffer
7456 .read(cx)
7457 .text_anchor_for_position(rename.range.end.clone(), cx)?;
7458 if start_buffer != end_buffer {
7459 return None;
7460 }
7461
7462 let buffer = start_buffer;
7463 let range = start..end;
7464 let old_name = rename.old_name;
7465 let new_name = rename.editor.read(cx).text(cx);
7466
7467 let rename = workspace
7468 .read(cx)
7469 .project()
7470 .clone()
7471 .update(cx, |project, cx| {
7472 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
7473 });
7474 let workspace = workspace.downgrade();
7475
7476 Some(cx.spawn(|editor, mut cx| async move {
7477 let project_transaction = rename.await?;
7478 Self::open_project_transaction(
7479 &editor,
7480 workspace,
7481 project_transaction,
7482 format!("Rename: {} → {}", old_name, new_name),
7483 cx.clone(),
7484 )
7485 .await?;
7486
7487 editor.update(&mut cx, |editor, cx| {
7488 editor.refresh_document_highlights(cx);
7489 })?;
7490 Ok(())
7491 }))
7492 }
7493
7494 fn take_rename(
7495 &mut self,
7496 moving_cursor: bool,
7497 cx: &mut ViewContext<Self>,
7498 ) -> Option<RenameState> {
7499 let rename = self.pending_rename.take()?;
7500 if rename.editor.focus_handle(cx).is_focused(cx) {
7501 cx.focus(&self.focus_handle);
7502 }
7503
7504 self.remove_blocks(
7505 [rename.block_id].into_iter().collect(),
7506 Some(Autoscroll::fit()),
7507 cx,
7508 );
7509 self.clear_highlights::<Rename>(cx);
7510 self.show_local_selections = true;
7511
7512 if moving_cursor {
7513 let rename_editor = rename.editor.read(cx);
7514 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
7515
7516 // Update the selection to match the position of the selection inside
7517 // the rename editor.
7518 let snapshot = self.buffer.read(cx).read(cx);
7519 let rename_range = rename.range.to_offset(&snapshot);
7520 let cursor_in_editor = snapshot
7521 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
7522 .min(rename_range.end);
7523 drop(snapshot);
7524
7525 self.change_selections(None, cx, |s| {
7526 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
7527 });
7528 } else {
7529 self.refresh_document_highlights(cx);
7530 }
7531
7532 Some(rename)
7533 }
7534
7535 #[cfg(any(test, feature = "test-support"))]
7536 pub fn pending_rename(&self) -> Option<&RenameState> {
7537 self.pending_rename.as_ref()
7538 }
7539
7540 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
7541 let project = match &self.project {
7542 Some(project) => project.clone(),
7543 None => return None,
7544 };
7545
7546 Some(self.perform_format(project, FormatTrigger::Manual, cx))
7547 }
7548
7549 fn perform_format(
7550 &mut self,
7551 project: Model<Project>,
7552 trigger: FormatTrigger,
7553 cx: &mut ViewContext<Self>,
7554 ) -> Task<Result<()>> {
7555 let buffer = self.buffer().clone();
7556 let buffers = buffer.read(cx).all_buffers();
7557
7558 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
7559 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
7560
7561 cx.spawn(|_, mut cx| async move {
7562 let transaction = futures::select_biased! {
7563 _ = timeout => {
7564 log::warn!("timed out waiting for formatting");
7565 None
7566 }
7567 transaction = format.log_err().fuse() => transaction,
7568 };
7569
7570 buffer
7571 .update(&mut cx, |buffer, cx| {
7572 if let Some(transaction) = transaction {
7573 if !buffer.is_singleton() {
7574 buffer.push_transaction(&transaction.0, cx);
7575 }
7576 }
7577
7578 cx.notify();
7579 })
7580 .ok();
7581
7582 Ok(())
7583 })
7584 }
7585
7586 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
7587 if let Some(project) = self.project.clone() {
7588 self.buffer.update(cx, |multi_buffer, cx| {
7589 project.update(cx, |project, cx| {
7590 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
7591 });
7592 })
7593 }
7594 }
7595
7596 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
7597 cx.show_character_palette();
7598 }
7599
7600 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
7601 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
7602 let buffer = self.buffer.read(cx).snapshot(cx);
7603 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
7604 let is_valid = buffer
7605 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
7606 .any(|entry| {
7607 entry.diagnostic.is_primary
7608 && !entry.range.is_empty()
7609 && entry.range.start == primary_range_start
7610 && entry.diagnostic.message == active_diagnostics.primary_message
7611 });
7612
7613 if is_valid != active_diagnostics.is_valid {
7614 active_diagnostics.is_valid = is_valid;
7615 let mut new_styles = HashMap::default();
7616 for (block_id, diagnostic) in &active_diagnostics.blocks {
7617 new_styles.insert(
7618 *block_id,
7619 diagnostic_block_renderer(diagnostic.clone(), is_valid),
7620 );
7621 }
7622 self.display_map
7623 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
7624 }
7625 }
7626 }
7627
7628 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
7629 self.dismiss_diagnostics(cx);
7630 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
7631 let buffer = self.buffer.read(cx).snapshot(cx);
7632
7633 let mut primary_range = None;
7634 let mut primary_message = None;
7635 let mut group_end = Point::zero();
7636 let diagnostic_group = buffer
7637 .diagnostic_group::<Point>(group_id)
7638 .map(|entry| {
7639 if entry.range.end > group_end {
7640 group_end = entry.range.end;
7641 }
7642 if entry.diagnostic.is_primary {
7643 primary_range = Some(entry.range.clone());
7644 primary_message = Some(entry.diagnostic.message.clone());
7645 }
7646 entry
7647 })
7648 .collect::<Vec<_>>();
7649 let primary_range = primary_range?;
7650 let primary_message = primary_message?;
7651 let primary_range =
7652 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
7653
7654 let blocks = display_map
7655 .insert_blocks(
7656 diagnostic_group.iter().map(|entry| {
7657 let diagnostic = entry.diagnostic.clone();
7658 let message_height = diagnostic.message.lines().count() as u8;
7659 BlockProperties {
7660 style: BlockStyle::Fixed,
7661 position: buffer.anchor_after(entry.range.start),
7662 height: message_height,
7663 render: diagnostic_block_renderer(diagnostic, true),
7664 disposition: BlockDisposition::Below,
7665 }
7666 }),
7667 cx,
7668 )
7669 .into_iter()
7670 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
7671 .collect();
7672
7673 Some(ActiveDiagnosticGroup {
7674 primary_range,
7675 primary_message,
7676 blocks,
7677 is_valid: true,
7678 })
7679 });
7680 self.active_diagnostics.is_some()
7681 }
7682
7683 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
7684 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
7685 self.display_map.update(cx, |display_map, cx| {
7686 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
7687 });
7688 cx.notify();
7689 }
7690 }
7691
7692 pub fn set_selections_from_remote(
7693 &mut self,
7694 selections: Vec<Selection<Anchor>>,
7695 pending_selection: Option<Selection<Anchor>>,
7696 cx: &mut ViewContext<Self>,
7697 ) {
7698 let old_cursor_position = self.selections.newest_anchor().head();
7699 self.selections.change_with(cx, |s| {
7700 s.select_anchors(selections);
7701 if let Some(pending_selection) = pending_selection {
7702 s.set_pending(pending_selection, SelectMode::Character);
7703 } else {
7704 s.clear_pending();
7705 }
7706 });
7707 self.selections_did_change(false, &old_cursor_position, cx);
7708 }
7709
7710 fn push_to_selection_history(&mut self) {
7711 self.selection_history.push(SelectionHistoryEntry {
7712 selections: self.selections.disjoint_anchors(),
7713 select_next_state: self.select_next_state.clone(),
7714 select_prev_state: self.select_prev_state.clone(),
7715 add_selections_state: self.add_selections_state.clone(),
7716 });
7717 }
7718
7719 pub fn transact(
7720 &mut self,
7721 cx: &mut ViewContext<Self>,
7722 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
7723 ) -> Option<TransactionId> {
7724 self.start_transaction_at(Instant::now(), cx);
7725 update(self, cx);
7726 self.end_transaction_at(Instant::now(), cx)
7727 }
7728
7729 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
7730 self.end_selection(cx);
7731 if let Some(tx_id) = self
7732 .buffer
7733 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
7734 {
7735 self.selection_history
7736 .insert_transaction(tx_id, self.selections.disjoint_anchors());
7737 }
7738 }
7739
7740 fn end_transaction_at(
7741 &mut self,
7742 now: Instant,
7743 cx: &mut ViewContext<Self>,
7744 ) -> Option<TransactionId> {
7745 if let Some(tx_id) = self
7746 .buffer
7747 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
7748 {
7749 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
7750 *end_selections = Some(self.selections.disjoint_anchors());
7751 } else {
7752 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
7753 }
7754
7755 cx.emit(EditorEvent::Edited);
7756 Some(tx_id)
7757 } else {
7758 None
7759 }
7760 }
7761
7762 pub fn fold(&mut self, _: &actions::Fold, cx: &mut ViewContext<Self>) {
7763 let mut fold_ranges = Vec::new();
7764
7765 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7766
7767 let selections = self.selections.all_adjusted(cx);
7768 for selection in selections {
7769 let range = selection.range().sorted();
7770 let buffer_start_row = range.start.row;
7771
7772 for row in (0..=range.end.row).rev() {
7773 let fold_range = display_map.foldable_range(row);
7774
7775 if let Some(fold_range) = fold_range {
7776 if fold_range.end.row >= buffer_start_row {
7777 fold_ranges.push(fold_range);
7778 if row <= range.start.row {
7779 break;
7780 }
7781 }
7782 }
7783 }
7784 }
7785
7786 self.fold_ranges(fold_ranges, true, cx);
7787 }
7788
7789 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
7790 let buffer_row = fold_at.buffer_row;
7791 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7792
7793 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
7794 let autoscroll = self
7795 .selections
7796 .all::<Point>(cx)
7797 .iter()
7798 .any(|selection| fold_range.overlaps(&selection.range()));
7799
7800 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
7801 }
7802 }
7803
7804 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
7805 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7806 let buffer = &display_map.buffer_snapshot;
7807 let selections = self.selections.all::<Point>(cx);
7808 let ranges = selections
7809 .iter()
7810 .map(|s| {
7811 let range = s.display_range(&display_map).sorted();
7812 let mut start = range.start.to_point(&display_map);
7813 let mut end = range.end.to_point(&display_map);
7814 start.column = 0;
7815 end.column = buffer.line_len(end.row);
7816 start..end
7817 })
7818 .collect::<Vec<_>>();
7819
7820 self.unfold_ranges(ranges, true, true, cx);
7821 }
7822
7823 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
7824 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7825
7826 let intersection_range = Point::new(unfold_at.buffer_row, 0)
7827 ..Point::new(
7828 unfold_at.buffer_row,
7829 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
7830 );
7831
7832 let autoscroll = self
7833 .selections
7834 .all::<Point>(cx)
7835 .iter()
7836 .any(|selection| selection.range().overlaps(&intersection_range));
7837
7838 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
7839 }
7840
7841 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
7842 let selections = self.selections.all::<Point>(cx);
7843 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7844 let line_mode = self.selections.line_mode;
7845 let ranges = selections.into_iter().map(|s| {
7846 if line_mode {
7847 let start = Point::new(s.start.row, 0);
7848 let end = Point::new(s.end.row, display_map.buffer_snapshot.line_len(s.end.row));
7849 start..end
7850 } else {
7851 s.start..s.end
7852 }
7853 });
7854 self.fold_ranges(ranges, true, cx);
7855 }
7856
7857 pub fn fold_ranges<T: ToOffset + Clone>(
7858 &mut self,
7859 ranges: impl IntoIterator<Item = Range<T>>,
7860 auto_scroll: bool,
7861 cx: &mut ViewContext<Self>,
7862 ) {
7863 let mut ranges = ranges.into_iter().peekable();
7864 if ranges.peek().is_some() {
7865 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
7866
7867 if auto_scroll {
7868 self.request_autoscroll(Autoscroll::fit(), cx);
7869 }
7870
7871 cx.notify();
7872 }
7873 }
7874
7875 pub fn unfold_ranges<T: ToOffset + Clone>(
7876 &mut self,
7877 ranges: impl IntoIterator<Item = Range<T>>,
7878 inclusive: bool,
7879 auto_scroll: bool,
7880 cx: &mut ViewContext<Self>,
7881 ) {
7882 let mut ranges = ranges.into_iter().peekable();
7883 if ranges.peek().is_some() {
7884 self.display_map
7885 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
7886 if auto_scroll {
7887 self.request_autoscroll(Autoscroll::fit(), cx);
7888 }
7889
7890 cx.notify();
7891 }
7892 }
7893
7894 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut ViewContext<Self>) {
7895 if hovered != self.gutter_hovered {
7896 self.gutter_hovered = hovered;
7897 cx.notify();
7898 }
7899 }
7900
7901 pub fn insert_blocks(
7902 &mut self,
7903 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
7904 autoscroll: Option<Autoscroll>,
7905 cx: &mut ViewContext<Self>,
7906 ) -> Vec<BlockId> {
7907 let blocks = self
7908 .display_map
7909 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
7910 if let Some(autoscroll) = autoscroll {
7911 self.request_autoscroll(autoscroll, cx);
7912 }
7913 blocks
7914 }
7915
7916 pub fn replace_blocks(
7917 &mut self,
7918 blocks: HashMap<BlockId, RenderBlock>,
7919 autoscroll: Option<Autoscroll>,
7920 cx: &mut ViewContext<Self>,
7921 ) {
7922 self.display_map
7923 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
7924 if let Some(autoscroll) = autoscroll {
7925 self.request_autoscroll(autoscroll, cx);
7926 }
7927 }
7928
7929 pub fn remove_blocks(
7930 &mut self,
7931 block_ids: HashSet<BlockId>,
7932 autoscroll: Option<Autoscroll>,
7933 cx: &mut ViewContext<Self>,
7934 ) {
7935 self.display_map.update(cx, |display_map, cx| {
7936 display_map.remove_blocks(block_ids, cx)
7937 });
7938 if let Some(autoscroll) = autoscroll {
7939 self.request_autoscroll(autoscroll, cx);
7940 }
7941 }
7942
7943 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
7944 self.display_map
7945 .update(cx, |map, cx| map.snapshot(cx))
7946 .longest_row()
7947 }
7948
7949 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
7950 self.display_map
7951 .update(cx, |map, cx| map.snapshot(cx))
7952 .max_point()
7953 }
7954
7955 pub fn text(&self, cx: &AppContext) -> String {
7956 self.buffer.read(cx).read(cx).text()
7957 }
7958
7959 pub fn text_option(&self, cx: &AppContext) -> Option<String> {
7960 let text = self.text(cx);
7961 let text = text.trim();
7962
7963 if text.is_empty() {
7964 return None;
7965 }
7966
7967 Some(text.to_string())
7968 }
7969
7970 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
7971 self.transact(cx, |this, cx| {
7972 this.buffer
7973 .read(cx)
7974 .as_singleton()
7975 .expect("you can only call set_text on editors for singleton buffers")
7976 .update(cx, |buffer, cx| buffer.set_text(text, cx));
7977 });
7978 }
7979
7980 pub fn display_text(&self, cx: &mut AppContext) -> String {
7981 self.display_map
7982 .update(cx, |map, cx| map.snapshot(cx))
7983 .text()
7984 }
7985
7986 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
7987 let mut wrap_guides = smallvec::smallvec![];
7988
7989 if self.show_wrap_guides == Some(false) {
7990 return wrap_guides;
7991 }
7992
7993 let settings = self.buffer.read(cx).settings_at(0, cx);
7994 if settings.show_wrap_guides {
7995 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
7996 wrap_guides.push((soft_wrap as usize, true));
7997 }
7998 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
7999 }
8000
8001 wrap_guides
8002 }
8003
8004 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
8005 let settings = self.buffer.read(cx).settings_at(0, cx);
8006 let mode = self
8007 .soft_wrap_mode_override
8008 .unwrap_or_else(|| settings.soft_wrap);
8009 match mode {
8010 language_settings::SoftWrap::None => SoftWrap::None,
8011 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
8012 language_settings::SoftWrap::PreferredLineLength => {
8013 SoftWrap::Column(settings.preferred_line_length)
8014 }
8015 }
8016 }
8017
8018 pub fn set_soft_wrap_mode(
8019 &mut self,
8020 mode: language_settings::SoftWrap,
8021 cx: &mut ViewContext<Self>,
8022 ) {
8023 self.soft_wrap_mode_override = Some(mode);
8024 cx.notify();
8025 }
8026
8027 pub fn set_style(&mut self, style: EditorStyle, cx: &mut ViewContext<Self>) {
8028 let rem_size = cx.rem_size();
8029 self.display_map.update(cx, |map, cx| {
8030 map.set_font(
8031 style.text.font(),
8032 style.text.font_size.to_pixels(rem_size),
8033 cx,
8034 )
8035 });
8036 self.style = Some(style);
8037 }
8038
8039 #[cfg(any(test, feature = "test-support"))]
8040 pub fn style(&self) -> Option<&EditorStyle> {
8041 self.style.as_ref()
8042 }
8043
8044 // Called by the element. This method is not designed to be called outside of the editor
8045 // element's layout code because it does not notify when rewrapping is computed synchronously.
8046 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut AppContext) -> bool {
8047 self.display_map
8048 .update(cx, |map, cx| map.set_wrap_width(width, cx))
8049 }
8050
8051 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
8052 if self.soft_wrap_mode_override.is_some() {
8053 self.soft_wrap_mode_override.take();
8054 } else {
8055 let soft_wrap = match self.soft_wrap_mode(cx) {
8056 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
8057 SoftWrap::EditorWidth | SoftWrap::Column(_) => language_settings::SoftWrap::None,
8058 };
8059 self.soft_wrap_mode_override = Some(soft_wrap);
8060 }
8061 cx.notify();
8062 }
8063
8064 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
8065 self.show_gutter = show_gutter;
8066 cx.notify();
8067 }
8068
8069 pub fn set_show_wrap_guides(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
8070 self.show_wrap_guides = Some(show_gutter);
8071 cx.notify();
8072 }
8073
8074 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
8075 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
8076 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
8077 cx.reveal_path(&file.abs_path(cx));
8078 }
8079 }
8080 }
8081
8082 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
8083 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
8084 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
8085 if let Some(path) = file.abs_path(cx).to_str() {
8086 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
8087 }
8088 }
8089 }
8090 }
8091
8092 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
8093 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
8094 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
8095 if let Some(path) = file.path().to_str() {
8096 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
8097 }
8098 }
8099 }
8100 }
8101
8102 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
8103 self.highlighted_rows = rows;
8104 }
8105
8106 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
8107 self.highlighted_rows.clone()
8108 }
8109
8110 pub fn highlight_background<T: 'static>(
8111 &mut self,
8112 ranges: Vec<Range<Anchor>>,
8113 color_fetcher: fn(&ThemeColors) -> Hsla,
8114 cx: &mut ViewContext<Self>,
8115 ) {
8116 self.background_highlights
8117 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
8118 cx.notify();
8119 }
8120
8121 pub(crate) fn highlight_inlay_background<T: 'static>(
8122 &mut self,
8123 ranges: Vec<InlayHighlight>,
8124 color_fetcher: fn(&ThemeColors) -> Hsla,
8125 cx: &mut ViewContext<Self>,
8126 ) {
8127 // TODO: no actual highlights happen for inlays currently, find a way to do that
8128 self.inlay_background_highlights
8129 .insert(Some(TypeId::of::<T>()), (color_fetcher, ranges));
8130 cx.notify();
8131 }
8132
8133 pub fn clear_background_highlights<T: 'static>(
8134 &mut self,
8135 cx: &mut ViewContext<Self>,
8136 ) -> Option<BackgroundHighlight> {
8137 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>());
8138 let inlay_highlights = self
8139 .inlay_background_highlights
8140 .remove(&Some(TypeId::of::<T>()));
8141 if text_highlights.is_some() || inlay_highlights.is_some() {
8142 cx.notify();
8143 }
8144 text_highlights
8145 }
8146
8147 #[cfg(feature = "test-support")]
8148 pub fn all_text_background_highlights(
8149 &mut self,
8150 cx: &mut ViewContext<Self>,
8151 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
8152 let snapshot = self.snapshot(cx);
8153 let buffer = &snapshot.buffer_snapshot;
8154 let start = buffer.anchor_before(0);
8155 let end = buffer.anchor_after(buffer.len());
8156 let theme = cx.theme().colors();
8157 self.background_highlights_in_range(start..end, &snapshot, theme)
8158 }
8159
8160 fn document_highlights_for_position<'a>(
8161 &'a self,
8162 position: Anchor,
8163 buffer: &'a MultiBufferSnapshot,
8164 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
8165 let read_highlights = self
8166 .background_highlights
8167 .get(&TypeId::of::<DocumentHighlightRead>())
8168 .map(|h| &h.1);
8169 let write_highlights = self
8170 .background_highlights
8171 .get(&TypeId::of::<DocumentHighlightWrite>())
8172 .map(|h| &h.1);
8173 let left_position = position.bias_left(buffer);
8174 let right_position = position.bias_right(buffer);
8175 read_highlights
8176 .into_iter()
8177 .chain(write_highlights)
8178 .flat_map(move |ranges| {
8179 let start_ix = match ranges.binary_search_by(|probe| {
8180 let cmp = probe.end.cmp(&left_position, buffer);
8181 if cmp.is_ge() {
8182 Ordering::Greater
8183 } else {
8184 Ordering::Less
8185 }
8186 }) {
8187 Ok(i) | Err(i) => i,
8188 };
8189
8190 let right_position = right_position.clone();
8191 ranges[start_ix..]
8192 .iter()
8193 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
8194 })
8195 }
8196
8197 pub fn has_background_highlights<T: 'static>(&self) -> bool {
8198 self.background_highlights
8199 .get(&TypeId::of::<T>())
8200 .map_or(false, |(_, highlights)| !highlights.is_empty())
8201 }
8202
8203 pub fn background_highlights_in_range(
8204 &self,
8205 search_range: Range<Anchor>,
8206 display_snapshot: &DisplaySnapshot,
8207 theme: &ThemeColors,
8208 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
8209 let mut results = Vec::new();
8210 for (color_fetcher, ranges) in self.background_highlights.values() {
8211 let color = color_fetcher(theme);
8212 let start_ix = match ranges.binary_search_by(|probe| {
8213 let cmp = probe
8214 .end
8215 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
8216 if cmp.is_gt() {
8217 Ordering::Greater
8218 } else {
8219 Ordering::Less
8220 }
8221 }) {
8222 Ok(i) | Err(i) => i,
8223 };
8224 for range in &ranges[start_ix..] {
8225 if range
8226 .start
8227 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
8228 .is_ge()
8229 {
8230 break;
8231 }
8232
8233 let start = range.start.to_display_point(&display_snapshot);
8234 let end = range.end.to_display_point(&display_snapshot);
8235 results.push((start..end, color))
8236 }
8237 }
8238 results
8239 }
8240
8241 pub fn background_highlight_row_ranges<T: 'static>(
8242 &self,
8243 search_range: Range<Anchor>,
8244 display_snapshot: &DisplaySnapshot,
8245 count: usize,
8246 ) -> Vec<RangeInclusive<DisplayPoint>> {
8247 let mut results = Vec::new();
8248 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
8249 return vec![];
8250 };
8251
8252 let start_ix = match ranges.binary_search_by(|probe| {
8253 let cmp = probe
8254 .end
8255 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
8256 if cmp.is_gt() {
8257 Ordering::Greater
8258 } else {
8259 Ordering::Less
8260 }
8261 }) {
8262 Ok(i) | Err(i) => i,
8263 };
8264 let mut push_region = |start: Option<Point>, end: Option<Point>| {
8265 if let (Some(start_display), Some(end_display)) = (start, end) {
8266 results.push(
8267 start_display.to_display_point(display_snapshot)
8268 ..=end_display.to_display_point(display_snapshot),
8269 );
8270 }
8271 };
8272 let mut start_row: Option<Point> = None;
8273 let mut end_row: Option<Point> = None;
8274 if ranges.len() > count {
8275 return Vec::new();
8276 }
8277 for range in &ranges[start_ix..] {
8278 if range
8279 .start
8280 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
8281 .is_ge()
8282 {
8283 break;
8284 }
8285 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
8286 if let Some(current_row) = &end_row {
8287 if end.row == current_row.row {
8288 continue;
8289 }
8290 }
8291 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
8292 if start_row.is_none() {
8293 assert_eq!(end_row, None);
8294 start_row = Some(start);
8295 end_row = Some(end);
8296 continue;
8297 }
8298 if let Some(current_end) = end_row.as_mut() {
8299 if start.row > current_end.row + 1 {
8300 push_region(start_row, end_row);
8301 start_row = Some(start);
8302 end_row = Some(end);
8303 } else {
8304 // Merge two hunks.
8305 *current_end = end;
8306 }
8307 } else {
8308 unreachable!();
8309 }
8310 }
8311 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
8312 push_region(start_row, end_row);
8313 results
8314 }
8315
8316 pub fn highlight_text<T: 'static>(
8317 &mut self,
8318 ranges: Vec<Range<Anchor>>,
8319 style: HighlightStyle,
8320 cx: &mut ViewContext<Self>,
8321 ) {
8322 self.display_map.update(cx, |map, _| {
8323 map.highlight_text(TypeId::of::<T>(), ranges, style)
8324 });
8325 cx.notify();
8326 }
8327
8328 pub(crate) fn highlight_inlays<T: 'static>(
8329 &mut self,
8330 highlights: Vec<InlayHighlight>,
8331 style: HighlightStyle,
8332 cx: &mut ViewContext<Self>,
8333 ) {
8334 self.display_map.update(cx, |map, _| {
8335 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
8336 });
8337 cx.notify();
8338 }
8339
8340 pub fn text_highlights<'a, T: 'static>(
8341 &'a self,
8342 cx: &'a AppContext,
8343 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
8344 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
8345 }
8346
8347 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
8348 let cleared = self
8349 .display_map
8350 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
8351 if cleared {
8352 cx.notify();
8353 }
8354 }
8355
8356 pub fn show_local_cursors(&self, cx: &WindowContext) -> bool {
8357 (self.read_only(cx) || self.blink_manager.read(cx).visible())
8358 && self.focus_handle.is_focused(cx)
8359 }
8360
8361 fn on_buffer_changed(&mut self, _: Model<MultiBuffer>, cx: &mut ViewContext<Self>) {
8362 cx.notify();
8363 }
8364
8365 fn on_buffer_event(
8366 &mut self,
8367 multibuffer: Model<MultiBuffer>,
8368 event: &multi_buffer::Event,
8369 cx: &mut ViewContext<Self>,
8370 ) {
8371 match event {
8372 multi_buffer::Event::Edited {
8373 singleton_buffer_edited,
8374 } => {
8375 self.refresh_active_diagnostics(cx);
8376 self.refresh_code_actions(cx);
8377 if self.has_active_copilot_suggestion(cx) {
8378 self.update_visible_copilot_suggestion(cx);
8379 }
8380 cx.emit(EditorEvent::BufferEdited);
8381 cx.emit(SearchEvent::MatchesInvalidated);
8382
8383 if *singleton_buffer_edited {
8384 if let Some(project) = &self.project {
8385 let project = project.read(cx);
8386 let languages_affected = multibuffer
8387 .read(cx)
8388 .all_buffers()
8389 .into_iter()
8390 .filter_map(|buffer| {
8391 let buffer = buffer.read(cx);
8392 let language = buffer.language()?;
8393 if project.is_local()
8394 && project.language_servers_for_buffer(buffer, cx).count() == 0
8395 {
8396 None
8397 } else {
8398 Some(language)
8399 }
8400 })
8401 .cloned()
8402 .collect::<HashSet<_>>();
8403 if !languages_affected.is_empty() {
8404 self.refresh_inlay_hints(
8405 InlayHintRefreshReason::BufferEdited(languages_affected),
8406 cx,
8407 );
8408 }
8409 }
8410 }
8411
8412 let Some(project) = &self.project else { return };
8413 let telemetry = project.read(cx).client().telemetry().clone();
8414 telemetry.log_edit_event("editor");
8415 }
8416 multi_buffer::Event::ExcerptsAdded {
8417 buffer,
8418 predecessor,
8419 excerpts,
8420 } => {
8421 cx.emit(EditorEvent::ExcerptsAdded {
8422 buffer: buffer.clone(),
8423 predecessor: *predecessor,
8424 excerpts: excerpts.clone(),
8425 });
8426 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
8427 }
8428 multi_buffer::Event::ExcerptsRemoved { ids } => {
8429 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
8430 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
8431 }
8432 multi_buffer::Event::Reparsed => cx.emit(EditorEvent::Reparsed),
8433 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
8434 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
8435 multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
8436 cx.emit(EditorEvent::TitleChanged)
8437 }
8438 multi_buffer::Event::DiffBaseChanged => cx.emit(EditorEvent::DiffBaseChanged),
8439 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
8440 multi_buffer::Event::DiagnosticsUpdated => {
8441 self.refresh_active_diagnostics(cx);
8442 }
8443 _ => {}
8444 };
8445 }
8446
8447 fn on_display_map_changed(&mut self, _: Model<DisplayMap>, cx: &mut ViewContext<Self>) {
8448 cx.notify();
8449 }
8450
8451 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
8452 self.refresh_copilot_suggestions(true, cx);
8453 self.refresh_inlay_hints(
8454 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
8455 self.selections.newest_anchor().head(),
8456 &self.buffer.read(cx).snapshot(cx),
8457 cx,
8458 )),
8459 cx,
8460 );
8461 cx.notify();
8462 }
8463
8464 pub fn set_searchable(&mut self, searchable: bool) {
8465 self.searchable = searchable;
8466 }
8467
8468 pub fn searchable(&self) -> bool {
8469 self.searchable
8470 }
8471
8472 fn open_excerpts(&mut self, _: &OpenExcerpts, cx: &mut ViewContext<Self>) {
8473 let buffer = self.buffer.read(cx);
8474 if buffer.is_singleton() {
8475 cx.propagate();
8476 return;
8477 }
8478
8479 let Some(workspace) = self.workspace() else {
8480 cx.propagate();
8481 return;
8482 };
8483
8484 let mut new_selections_by_buffer = HashMap::default();
8485 for selection in self.selections.all::<usize>(cx) {
8486 for (buffer, mut range, _) in
8487 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
8488 {
8489 if selection.reversed {
8490 mem::swap(&mut range.start, &mut range.end);
8491 }
8492 new_selections_by_buffer
8493 .entry(buffer)
8494 .or_insert(Vec::new())
8495 .push(range)
8496 }
8497 }
8498
8499 self.push_to_nav_history(self.selections.newest_anchor().head(), None, cx);
8500
8501 // We defer the pane interaction because we ourselves are a workspace item
8502 // and activating a new item causes the pane to call a method on us reentrantly,
8503 // which panics if we're on the stack.
8504 cx.window_context().defer(move |cx| {
8505 workspace.update(cx, |workspace, cx| {
8506 let pane = workspace.active_pane().clone();
8507 pane.update(cx, |pane, _| pane.disable_history());
8508
8509 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
8510 let editor = workspace.open_project_item::<Self>(buffer, cx);
8511 editor.update(cx, |editor, cx| {
8512 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
8513 s.select_ranges(ranges);
8514 });
8515 });
8516 }
8517
8518 pane.update(cx, |pane, _| pane.enable_history());
8519 })
8520 });
8521 }
8522
8523 fn jump(
8524 &mut self,
8525 path: ProjectPath,
8526 position: Point,
8527 anchor: language::Anchor,
8528 cx: &mut ViewContext<Self>,
8529 ) {
8530 let workspace = self.workspace();
8531 cx.spawn(|_, mut cx| async move {
8532 let workspace = workspace.ok_or_else(|| anyhow!("cannot jump without workspace"))?;
8533 let editor = workspace.update(&mut cx, |workspace, cx| {
8534 workspace.open_path(path, None, true, cx)
8535 })?;
8536 let editor = editor
8537 .await?
8538 .downcast::<Editor>()
8539 .ok_or_else(|| anyhow!("opened item was not an editor"))?
8540 .downgrade();
8541 editor.update(&mut cx, |editor, cx| {
8542 let buffer = editor
8543 .buffer()
8544 .read(cx)
8545 .as_singleton()
8546 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
8547 let buffer = buffer.read(cx);
8548 let cursor = if buffer.can_resolve(&anchor) {
8549 language::ToPoint::to_point(&anchor, buffer)
8550 } else {
8551 buffer.clip_point(position, Bias::Left)
8552 };
8553
8554 let nav_history = editor.nav_history.take();
8555 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
8556 s.select_ranges([cursor..cursor]);
8557 });
8558 editor.nav_history = nav_history;
8559
8560 anyhow::Ok(())
8561 })??;
8562
8563 anyhow::Ok(())
8564 })
8565 .detach_and_log_err(cx);
8566 }
8567
8568 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
8569 let snapshot = self.buffer.read(cx).read(cx);
8570 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
8571 Some(
8572 ranges
8573 .iter()
8574 .map(move |range| {
8575 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
8576 })
8577 .collect(),
8578 )
8579 }
8580
8581 fn selection_replacement_ranges(
8582 &self,
8583 range: Range<OffsetUtf16>,
8584 cx: &AppContext,
8585 ) -> Vec<Range<OffsetUtf16>> {
8586 let selections = self.selections.all::<OffsetUtf16>(cx);
8587 let newest_selection = selections
8588 .iter()
8589 .max_by_key(|selection| selection.id)
8590 .unwrap();
8591 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
8592 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
8593 let snapshot = self.buffer.read(cx).read(cx);
8594 selections
8595 .into_iter()
8596 .map(|mut selection| {
8597 selection.start.0 =
8598 (selection.start.0 as isize).saturating_add(start_delta) as usize;
8599 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
8600 snapshot.clip_offset_utf16(selection.start, Bias::Left)
8601 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
8602 })
8603 .collect()
8604 }
8605
8606 fn report_copilot_event(
8607 &self,
8608 suggestion_id: Option<String>,
8609 suggestion_accepted: bool,
8610 cx: &AppContext,
8611 ) {
8612 let Some(project) = &self.project else { return };
8613
8614 // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension
8615 let file_extension = self
8616 .buffer
8617 .read(cx)
8618 .as_singleton()
8619 .and_then(|b| b.read(cx).file())
8620 .and_then(|file| Path::new(file.file_name(cx)).extension())
8621 .and_then(|e| e.to_str())
8622 .map(|a| a.to_string());
8623
8624 let telemetry = project.read(cx).client().telemetry().clone();
8625
8626 telemetry.report_copilot_event(suggestion_id, suggestion_accepted, file_extension)
8627 }
8628
8629 #[cfg(any(test, feature = "test-support"))]
8630 fn report_editor_event(
8631 &self,
8632 _operation: &'static str,
8633 _file_extension: Option<String>,
8634 _cx: &AppContext,
8635 ) {
8636 }
8637
8638 #[cfg(not(any(test, feature = "test-support")))]
8639 fn report_editor_event(
8640 &self,
8641 operation: &'static str,
8642 file_extension: Option<String>,
8643 cx: &AppContext,
8644 ) {
8645 let Some(project) = &self.project else { return };
8646
8647 // If None, we are in a file without an extension
8648 let file = self
8649 .buffer
8650 .read(cx)
8651 .as_singleton()
8652 .and_then(|b| b.read(cx).file());
8653 let file_extension = file_extension.or(file
8654 .as_ref()
8655 .and_then(|file| Path::new(file.file_name(cx)).extension())
8656 .and_then(|e| e.to_str())
8657 .map(|a| a.to_string()));
8658
8659 let vim_mode = cx
8660 .global::<SettingsStore>()
8661 .raw_user_settings()
8662 .get("vim_mode")
8663 == Some(&serde_json::Value::Bool(true));
8664 let copilot_enabled = all_language_settings(file, cx).copilot_enabled(None, None);
8665 let copilot_enabled_for_language = self
8666 .buffer
8667 .read(cx)
8668 .settings_at(0, cx)
8669 .show_copilot_suggestions;
8670
8671 let telemetry = project.read(cx).client().telemetry().clone();
8672 telemetry.report_editor_event(
8673 file_extension,
8674 vim_mode,
8675 operation,
8676 copilot_enabled,
8677 copilot_enabled_for_language,
8678 )
8679 }
8680
8681 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
8682 /// with each line being an array of {text, highlight} objects.
8683 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
8684 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
8685 return;
8686 };
8687
8688 #[derive(Serialize)]
8689 struct Chunk<'a> {
8690 text: String,
8691 highlight: Option<&'a str>,
8692 }
8693
8694 let snapshot = buffer.read(cx).snapshot();
8695 let range = self
8696 .selected_text_range(cx)
8697 .and_then(|selected_range| {
8698 if selected_range.is_empty() {
8699 None
8700 } else {
8701 Some(selected_range)
8702 }
8703 })
8704 .unwrap_or_else(|| 0..snapshot.len());
8705
8706 let chunks = snapshot.chunks(range, true);
8707 let mut lines = Vec::new();
8708 let mut line: VecDeque<Chunk> = VecDeque::new();
8709
8710 let Some(style) = self.style.as_ref() else {
8711 return;
8712 };
8713
8714 for chunk in chunks {
8715 let highlight = chunk
8716 .syntax_highlight_id
8717 .and_then(|id| id.name(&style.syntax));
8718 let mut chunk_lines = chunk.text.split("\n").peekable();
8719 while let Some(text) = chunk_lines.next() {
8720 let mut merged_with_last_token = false;
8721 if let Some(last_token) = line.back_mut() {
8722 if last_token.highlight == highlight {
8723 last_token.text.push_str(text);
8724 merged_with_last_token = true;
8725 }
8726 }
8727
8728 if !merged_with_last_token {
8729 line.push_back(Chunk {
8730 text: text.into(),
8731 highlight,
8732 });
8733 }
8734
8735 if chunk_lines.peek().is_some() {
8736 if line.len() > 1 && line.front().unwrap().text.is_empty() {
8737 line.pop_front();
8738 }
8739 if line.len() > 1 && line.back().unwrap().text.is_empty() {
8740 line.pop_back();
8741 }
8742
8743 lines.push(mem::take(&mut line));
8744 }
8745 }
8746 }
8747
8748 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
8749 return;
8750 };
8751 cx.write_to_clipboard(ClipboardItem::new(lines));
8752 }
8753
8754 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
8755 &self.inlay_hint_cache
8756 }
8757
8758 pub fn replay_insert_event(
8759 &mut self,
8760 text: &str,
8761 relative_utf16_range: Option<Range<isize>>,
8762 cx: &mut ViewContext<Self>,
8763 ) {
8764 if !self.input_enabled {
8765 cx.emit(EditorEvent::InputIgnored { text: text.into() });
8766 return;
8767 }
8768 if let Some(relative_utf16_range) = relative_utf16_range {
8769 let selections = self.selections.all::<OffsetUtf16>(cx);
8770 self.change_selections(None, cx, |s| {
8771 let new_ranges = selections.into_iter().map(|range| {
8772 let start = OffsetUtf16(
8773 range
8774 .head()
8775 .0
8776 .saturating_add_signed(relative_utf16_range.start),
8777 );
8778 let end = OffsetUtf16(
8779 range
8780 .head()
8781 .0
8782 .saturating_add_signed(relative_utf16_range.end),
8783 );
8784 start..end
8785 });
8786 s.select_ranges(new_ranges);
8787 });
8788 }
8789
8790 self.handle_input(text, cx);
8791 }
8792
8793 pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
8794 let Some(project) = self.project.as_ref() else {
8795 return false;
8796 };
8797 let project = project.read(cx);
8798
8799 let mut supports = false;
8800 self.buffer().read(cx).for_each_buffer(|buffer| {
8801 if !supports {
8802 supports = project
8803 .language_servers_for_buffer(buffer.read(cx), cx)
8804 .any(
8805 |(_, server)| match server.capabilities().inlay_hint_provider {
8806 Some(lsp::OneOf::Left(enabled)) => enabled,
8807 Some(lsp::OneOf::Right(_)) => true,
8808 None => false,
8809 },
8810 )
8811 }
8812 });
8813 supports
8814 }
8815
8816 pub fn focus(&self, cx: &mut WindowContext) {
8817 cx.focus(&self.focus_handle)
8818 }
8819
8820 pub fn is_focused(&self, cx: &WindowContext) -> bool {
8821 self.focus_handle.is_focused(cx)
8822 }
8823
8824 fn handle_focus(&mut self, cx: &mut ViewContext<Self>) {
8825 cx.emit(EditorEvent::Focused);
8826
8827 if let Some(rename) = self.pending_rename.as_ref() {
8828 let rename_editor_focus_handle = rename.editor.read(cx).focus_handle.clone();
8829 cx.focus(&rename_editor_focus_handle);
8830 } else {
8831 self.blink_manager.update(cx, BlinkManager::enable);
8832 self.show_cursor_names(cx);
8833 self.buffer.update(cx, |buffer, cx| {
8834 buffer.finalize_last_transaction(cx);
8835 if self.leader_peer_id.is_none() {
8836 buffer.set_active_selections(
8837 &self.selections.disjoint_anchors(),
8838 self.selections.line_mode,
8839 self.cursor_shape,
8840 cx,
8841 );
8842 }
8843 });
8844 }
8845 }
8846
8847 pub fn handle_blur(&mut self, cx: &mut ViewContext<Self>) {
8848 self.blink_manager.update(cx, BlinkManager::disable);
8849 self.buffer
8850 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
8851 self.hide_context_menu(cx);
8852 hide_hover(self, cx);
8853 cx.emit(EditorEvent::Blurred);
8854 cx.notify();
8855 }
8856
8857 pub fn register_action<A: Action>(
8858 &mut self,
8859 listener: impl Fn(&A, &mut WindowContext) + 'static,
8860 ) -> &mut Self {
8861 let listener = Arc::new(listener);
8862
8863 self.editor_actions.push(Box::new(move |cx| {
8864 let _view = cx.view().clone();
8865 let cx = cx.window_context();
8866 let listener = listener.clone();
8867 cx.on_action(TypeId::of::<A>(), move |action, phase, cx| {
8868 let action = action.downcast_ref().unwrap();
8869 if phase == DispatchPhase::Bubble {
8870 listener(action, cx)
8871 }
8872 })
8873 }));
8874 self
8875 }
8876}
8877
8878pub trait CollaborationHub {
8879 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
8880 fn user_participant_indices<'a>(
8881 &self,
8882 cx: &'a AppContext,
8883 ) -> &'a HashMap<u64, ParticipantIndex>;
8884 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString>;
8885}
8886
8887impl CollaborationHub for Model<Project> {
8888 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
8889 self.read(cx).collaborators()
8890 }
8891
8892 fn user_participant_indices<'a>(
8893 &self,
8894 cx: &'a AppContext,
8895 ) -> &'a HashMap<u64, ParticipantIndex> {
8896 self.read(cx).user_store().read(cx).participant_indices()
8897 }
8898
8899 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString> {
8900 let this = self.read(cx);
8901 let user_ids = this.collaborators().values().map(|c| c.user_id);
8902 this.user_store().read_with(cx, |user_store, cx| {
8903 user_store.participant_names(user_ids, cx)
8904 })
8905 }
8906}
8907
8908pub trait CompletionProvider {
8909 fn completions(
8910 &self,
8911 buffer: &Model<Buffer>,
8912 buffer_position: text::Anchor,
8913 cx: &mut ViewContext<Editor>,
8914 ) -> Task<Result<Vec<Completion>>>;
8915 fn resolve_completions(
8916 &self,
8917 completion_indices: Vec<usize>,
8918 completions: Arc<RwLock<Box<[Completion]>>>,
8919 cx: &mut ViewContext<Editor>,
8920 ) -> Task<Result<bool>>;
8921}
8922
8923impl CompletionProvider for Model<Project> {
8924 fn completions(
8925 &self,
8926 buffer: &Model<Buffer>,
8927 buffer_position: text::Anchor,
8928 cx: &mut ViewContext<Editor>,
8929 ) -> Task<Result<Vec<Completion>>> {
8930 self.update(cx, |project, cx| {
8931 project.completions(&buffer, buffer_position, cx)
8932 })
8933 }
8934
8935 fn resolve_completions(
8936 &self,
8937 completion_indices: Vec<usize>,
8938 completions: Arc<RwLock<Box<[Completion]>>>,
8939 cx: &mut ViewContext<Editor>,
8940 ) -> Task<Result<bool>> {
8941 self.update(cx, |project, cx| {
8942 project.resolve_completions(completion_indices, completions, cx)
8943 })
8944 }
8945}
8946
8947fn inlay_hint_settings(
8948 location: Anchor,
8949 snapshot: &MultiBufferSnapshot,
8950 cx: &mut ViewContext<'_, Editor>,
8951) -> InlayHintSettings {
8952 let file = snapshot.file_at(location);
8953 let language = snapshot.language_at(location);
8954 let settings = all_language_settings(file, cx);
8955 settings
8956 .language(language.map(|l| l.name()).as_deref())
8957 .inlay_hints
8958}
8959
8960fn consume_contiguous_rows(
8961 contiguous_row_selections: &mut Vec<Selection<Point>>,
8962 selection: &Selection<Point>,
8963 display_map: &DisplaySnapshot,
8964 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
8965) -> (u32, u32) {
8966 contiguous_row_selections.push(selection.clone());
8967 let start_row = selection.start.row;
8968 let mut end_row = ending_row(selection, display_map);
8969
8970 while let Some(next_selection) = selections.peek() {
8971 if next_selection.start.row <= end_row {
8972 end_row = ending_row(next_selection, display_map);
8973 contiguous_row_selections.push(selections.next().unwrap().clone());
8974 } else {
8975 break;
8976 }
8977 }
8978 (start_row, end_row)
8979}
8980
8981fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
8982 if next_selection.end.column > 0 || next_selection.is_empty() {
8983 display_map.next_line_boundary(next_selection.end).0.row + 1
8984 } else {
8985 next_selection.end.row
8986 }
8987}
8988
8989impl EditorSnapshot {
8990 pub fn remote_selections_in_range<'a>(
8991 &'a self,
8992 range: &'a Range<Anchor>,
8993 collaboration_hub: &dyn CollaborationHub,
8994 cx: &'a AppContext,
8995 ) -> impl 'a + Iterator<Item = RemoteSelection> {
8996 let participant_names = collaboration_hub.user_names(cx);
8997 let participant_indices = collaboration_hub.user_participant_indices(cx);
8998 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
8999 let collaborators_by_replica_id = collaborators_by_peer_id
9000 .iter()
9001 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
9002 .collect::<HashMap<_, _>>();
9003 self.buffer_snapshot
9004 .remote_selections_in_range(range)
9005 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
9006 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
9007 let participant_index = participant_indices.get(&collaborator.user_id).copied();
9008 let user_name = participant_names.get(&collaborator.user_id).cloned();
9009 Some(RemoteSelection {
9010 replica_id,
9011 selection,
9012 cursor_shape,
9013 line_mode,
9014 participant_index,
9015 peer_id: collaborator.peer_id,
9016 user_name,
9017 })
9018 })
9019 }
9020
9021 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
9022 self.display_snapshot.buffer_snapshot.language_at(position)
9023 }
9024
9025 pub fn is_focused(&self) -> bool {
9026 self.is_focused
9027 }
9028
9029 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
9030 self.placeholder_text.as_ref()
9031 }
9032
9033 pub fn scroll_position(&self) -> gpui::Point<f32> {
9034 self.scroll_anchor.scroll_position(&self.display_snapshot)
9035 }
9036}
9037
9038impl Deref for EditorSnapshot {
9039 type Target = DisplaySnapshot;
9040
9041 fn deref(&self) -> &Self::Target {
9042 &self.display_snapshot
9043 }
9044}
9045
9046#[derive(Clone, Debug, PartialEq, Eq)]
9047pub enum EditorEvent {
9048 InputIgnored {
9049 text: Arc<str>,
9050 },
9051 InputHandled {
9052 utf16_range_to_replace: Option<Range<isize>>,
9053 text: Arc<str>,
9054 },
9055 ExcerptsAdded {
9056 buffer: Model<Buffer>,
9057 predecessor: ExcerptId,
9058 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
9059 },
9060 ExcerptsRemoved {
9061 ids: Vec<ExcerptId>,
9062 },
9063 BufferEdited,
9064 Edited,
9065 Reparsed,
9066 Focused,
9067 Blurred,
9068 DirtyChanged,
9069 Saved,
9070 TitleChanged,
9071 DiffBaseChanged,
9072 SelectionsChanged {
9073 local: bool,
9074 },
9075 ScrollPositionChanged {
9076 local: bool,
9077 autoscroll: bool,
9078 },
9079 Closed,
9080}
9081
9082impl EventEmitter<EditorEvent> for Editor {}
9083
9084impl FocusableView for Editor {
9085 fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
9086 self.focus_handle.clone()
9087 }
9088}
9089
9090impl Render for Editor {
9091 fn render<'a>(&mut self, cx: &mut ViewContext<'a, Self>) -> impl IntoElement {
9092 let settings = ThemeSettings::get_global(cx);
9093 let text_style = match self.mode {
9094 EditorMode::SingleLine | EditorMode::AutoHeight { .. } => TextStyle {
9095 color: cx.theme().colors().editor_foreground,
9096 font_family: settings.ui_font.family.clone(),
9097 font_features: settings.ui_font.features,
9098 font_size: rems(0.875).into(),
9099 font_weight: FontWeight::NORMAL,
9100 font_style: FontStyle::Normal,
9101 line_height: relative(settings.buffer_line_height.value()),
9102 background_color: None,
9103 underline: None,
9104 white_space: WhiteSpace::Normal,
9105 },
9106
9107 EditorMode::Full => TextStyle {
9108 color: cx.theme().colors().editor_foreground,
9109 font_family: settings.buffer_font.family.clone(),
9110 font_features: settings.buffer_font.features,
9111 font_size: settings.buffer_font_size(cx).into(),
9112 font_weight: FontWeight::NORMAL,
9113 font_style: FontStyle::Normal,
9114 line_height: relative(settings.buffer_line_height.value()),
9115 background_color: None,
9116 underline: None,
9117 white_space: WhiteSpace::Normal,
9118 },
9119 };
9120
9121 let background = match self.mode {
9122 EditorMode::SingleLine => cx.theme().system().transparent,
9123 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
9124 EditorMode::Full => cx.theme().colors().editor_background,
9125 };
9126
9127 EditorElement::new(
9128 cx.view(),
9129 EditorStyle {
9130 background,
9131 local_player: cx.theme().players().local(),
9132 text: text_style,
9133 scrollbar_width: px(12.),
9134 syntax: cx.theme().syntax().clone(),
9135 status: cx.theme().status().clone(),
9136 inlays_style: HighlightStyle {
9137 color: Some(cx.theme().status().hint),
9138 font_weight: Some(FontWeight::BOLD),
9139 ..HighlightStyle::default()
9140 },
9141 suggestions_style: HighlightStyle {
9142 color: Some(cx.theme().status().predictive),
9143 ..HighlightStyle::default()
9144 },
9145 },
9146 )
9147 }
9148}
9149
9150impl InputHandler for Editor {
9151 fn text_for_range(
9152 &mut self,
9153 range_utf16: Range<usize>,
9154 cx: &mut ViewContext<Self>,
9155 ) -> Option<String> {
9156 Some(
9157 self.buffer
9158 .read(cx)
9159 .read(cx)
9160 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
9161 .collect(),
9162 )
9163 }
9164
9165 fn selected_text_range(&mut self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
9166 // Prevent the IME menu from appearing when holding down an alphabetic key
9167 // while input is disabled.
9168 if !self.input_enabled {
9169 return None;
9170 }
9171
9172 let range = self.selections.newest::<OffsetUtf16>(cx).range();
9173 Some(range.start.0..range.end.0)
9174 }
9175
9176 fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
9177 let snapshot = self.buffer.read(cx).read(cx);
9178 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
9179 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
9180 }
9181
9182 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
9183 self.clear_highlights::<InputComposition>(cx);
9184 self.ime_transaction.take();
9185 }
9186
9187 fn replace_text_in_range(
9188 &mut self,
9189 range_utf16: Option<Range<usize>>,
9190 text: &str,
9191 cx: &mut ViewContext<Self>,
9192 ) {
9193 if !self.input_enabled {
9194 cx.emit(EditorEvent::InputIgnored { text: text.into() });
9195 return;
9196 }
9197
9198 self.transact(cx, |this, cx| {
9199 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
9200 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
9201 Some(this.selection_replacement_ranges(range_utf16, cx))
9202 } else {
9203 this.marked_text_ranges(cx)
9204 };
9205
9206 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
9207 let newest_selection_id = this.selections.newest_anchor().id;
9208 this.selections
9209 .all::<OffsetUtf16>(cx)
9210 .iter()
9211 .zip(ranges_to_replace.iter())
9212 .find_map(|(selection, range)| {
9213 if selection.id == newest_selection_id {
9214 Some(
9215 (range.start.0 as isize - selection.head().0 as isize)
9216 ..(range.end.0 as isize - selection.head().0 as isize),
9217 )
9218 } else {
9219 None
9220 }
9221 })
9222 });
9223
9224 cx.emit(EditorEvent::InputHandled {
9225 utf16_range_to_replace: range_to_replace,
9226 text: text.into(),
9227 });
9228
9229 if let Some(new_selected_ranges) = new_selected_ranges {
9230 this.change_selections(None, cx, |selections| {
9231 selections.select_ranges(new_selected_ranges)
9232 });
9233 }
9234
9235 this.handle_input(text, cx);
9236 });
9237
9238 if let Some(transaction) = self.ime_transaction {
9239 self.buffer.update(cx, |buffer, cx| {
9240 buffer.group_until_transaction(transaction, cx);
9241 });
9242 }
9243
9244 self.unmark_text(cx);
9245 }
9246
9247 fn replace_and_mark_text_in_range(
9248 &mut self,
9249 range_utf16: Option<Range<usize>>,
9250 text: &str,
9251 new_selected_range_utf16: Option<Range<usize>>,
9252 cx: &mut ViewContext<Self>,
9253 ) {
9254 if !self.input_enabled {
9255 cx.emit(EditorEvent::InputIgnored { text: text.into() });
9256 return;
9257 }
9258
9259 let transaction = self.transact(cx, |this, cx| {
9260 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
9261 let snapshot = this.buffer.read(cx).read(cx);
9262 if let Some(relative_range_utf16) = range_utf16.as_ref() {
9263 for marked_range in &mut marked_ranges {
9264 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
9265 marked_range.start.0 += relative_range_utf16.start;
9266 marked_range.start =
9267 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
9268 marked_range.end =
9269 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
9270 }
9271 }
9272 Some(marked_ranges)
9273 } else if let Some(range_utf16) = range_utf16 {
9274 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
9275 Some(this.selection_replacement_ranges(range_utf16, cx))
9276 } else {
9277 None
9278 };
9279
9280 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
9281 let newest_selection_id = this.selections.newest_anchor().id;
9282 this.selections
9283 .all::<OffsetUtf16>(cx)
9284 .iter()
9285 .zip(ranges_to_replace.iter())
9286 .find_map(|(selection, range)| {
9287 if selection.id == newest_selection_id {
9288 Some(
9289 (range.start.0 as isize - selection.head().0 as isize)
9290 ..(range.end.0 as isize - selection.head().0 as isize),
9291 )
9292 } else {
9293 None
9294 }
9295 })
9296 });
9297
9298 cx.emit(EditorEvent::InputHandled {
9299 utf16_range_to_replace: range_to_replace,
9300 text: text.into(),
9301 });
9302
9303 if let Some(ranges) = ranges_to_replace {
9304 this.change_selections(None, cx, |s| s.select_ranges(ranges));
9305 }
9306
9307 let marked_ranges = {
9308 let snapshot = this.buffer.read(cx).read(cx);
9309 this.selections
9310 .disjoint_anchors()
9311 .iter()
9312 .map(|selection| {
9313 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
9314 })
9315 .collect::<Vec<_>>()
9316 };
9317
9318 if text.is_empty() {
9319 this.unmark_text(cx);
9320 } else {
9321 this.highlight_text::<InputComposition>(
9322 marked_ranges.clone(),
9323 HighlightStyle::default(), // todo!() this.style(cx).composition_mark,
9324 cx,
9325 );
9326 }
9327
9328 this.handle_input(text, cx);
9329
9330 if let Some(new_selected_range) = new_selected_range_utf16 {
9331 let snapshot = this.buffer.read(cx).read(cx);
9332 let new_selected_ranges = marked_ranges
9333 .into_iter()
9334 .map(|marked_range| {
9335 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
9336 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
9337 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
9338 snapshot.clip_offset_utf16(new_start, Bias::Left)
9339 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
9340 })
9341 .collect::<Vec<_>>();
9342
9343 drop(snapshot);
9344 this.change_selections(None, cx, |selections| {
9345 selections.select_ranges(new_selected_ranges)
9346 });
9347 }
9348 });
9349
9350 self.ime_transaction = self.ime_transaction.or(transaction);
9351 if let Some(transaction) = self.ime_transaction {
9352 self.buffer.update(cx, |buffer, cx| {
9353 buffer.group_until_transaction(transaction, cx);
9354 });
9355 }
9356
9357 if self.text_highlights::<InputComposition>(cx).is_none() {
9358 self.ime_transaction.take();
9359 }
9360 }
9361
9362 fn bounds_for_range(
9363 &mut self,
9364 range_utf16: Range<usize>,
9365 element_bounds: gpui::Bounds<Pixels>,
9366 cx: &mut ViewContext<Self>,
9367 ) -> Option<gpui::Bounds<Pixels>> {
9368 let text_layout_details = self.text_layout_details(cx);
9369 let style = &text_layout_details.editor_style;
9370 let font_id = cx.text_system().resolve_font(&style.text.font());
9371 let font_size = style.text.font_size.to_pixels(cx.rem_size());
9372 let line_height = style.text.line_height_in_pixels(cx.rem_size());
9373 let em_width = cx
9374 .text_system()
9375 .typographic_bounds(font_id, font_size, 'm')
9376 .unwrap()
9377 .size
9378 .width;
9379
9380 let snapshot = self.snapshot(cx);
9381 let scroll_position = snapshot.scroll_position();
9382 let scroll_left = scroll_position.x * em_width;
9383
9384 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
9385 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
9386 + self.gutter_width;
9387 let y = line_height * (start.row() as f32 - scroll_position.y);
9388
9389 Some(Bounds {
9390 origin: element_bounds.origin + point(x, y),
9391 size: size(em_width, line_height),
9392 })
9393 }
9394}
9395
9396trait SelectionExt {
9397 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
9398 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
9399 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
9400 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
9401 -> Range<u32>;
9402}
9403
9404impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
9405 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
9406 let start = self.start.to_point(buffer);
9407 let end = self.end.to_point(buffer);
9408 if self.reversed {
9409 end..start
9410 } else {
9411 start..end
9412 }
9413 }
9414
9415 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
9416 let start = self.start.to_offset(buffer);
9417 let end = self.end.to_offset(buffer);
9418 if self.reversed {
9419 end..start
9420 } else {
9421 start..end
9422 }
9423 }
9424
9425 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
9426 let start = self
9427 .start
9428 .to_point(&map.buffer_snapshot)
9429 .to_display_point(map);
9430 let end = self
9431 .end
9432 .to_point(&map.buffer_snapshot)
9433 .to_display_point(map);
9434 if self.reversed {
9435 end..start
9436 } else {
9437 start..end
9438 }
9439 }
9440
9441 fn spanned_rows(
9442 &self,
9443 include_end_if_at_line_start: bool,
9444 map: &DisplaySnapshot,
9445 ) -> Range<u32> {
9446 let start = self.start.to_point(&map.buffer_snapshot);
9447 let mut end = self.end.to_point(&map.buffer_snapshot);
9448 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
9449 end.row -= 1;
9450 }
9451
9452 let buffer_start = map.prev_line_boundary(start).0;
9453 let buffer_end = map.next_line_boundary(end).0;
9454 buffer_start.row..buffer_end.row + 1
9455 }
9456}
9457
9458impl<T: InvalidationRegion> InvalidationStack<T> {
9459 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
9460 where
9461 S: Clone + ToOffset,
9462 {
9463 while let Some(region) = self.last() {
9464 let all_selections_inside_invalidation_ranges =
9465 if selections.len() == region.ranges().len() {
9466 selections
9467 .iter()
9468 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
9469 .all(|(selection, invalidation_range)| {
9470 let head = selection.head().to_offset(buffer);
9471 invalidation_range.start <= head && invalidation_range.end >= head
9472 })
9473 } else {
9474 false
9475 };
9476
9477 if all_selections_inside_invalidation_ranges {
9478 break;
9479 } else {
9480 self.pop();
9481 }
9482 }
9483 }
9484}
9485
9486impl<T> Default for InvalidationStack<T> {
9487 fn default() -> Self {
9488 Self(Default::default())
9489 }
9490}
9491
9492impl<T> Deref for InvalidationStack<T> {
9493 type Target = Vec<T>;
9494
9495 fn deref(&self) -> &Self::Target {
9496 &self.0
9497 }
9498}
9499
9500impl<T> DerefMut for InvalidationStack<T> {
9501 fn deref_mut(&mut self) -> &mut Self::Target {
9502 &mut self.0
9503 }
9504}
9505
9506impl InvalidationRegion for SnippetState {
9507 fn ranges(&self) -> &[Range<Anchor>] {
9508 &self.ranges[self.active_index]
9509 }
9510}
9511
9512pub fn diagnostic_block_renderer(diagnostic: Diagnostic, _is_valid: bool) -> RenderBlock {
9513 let (text_without_backticks, code_ranges) = highlight_diagnostic_message(&diagnostic);
9514
9515 Arc::new(move |cx: &mut BlockContext| {
9516 let color = Some(cx.theme().colors().text_accent);
9517 let group_id: SharedString = cx.block_id.to_string().into();
9518 // TODO: Nate: We should tint the background of the block with the severity color
9519 // We need to extend the theme before we can do this
9520 h_flex()
9521 .id(cx.block_id)
9522 .group(group_id.clone())
9523 .relative()
9524 .pl(cx.anchor_x)
9525 .size_full()
9526 .gap_2()
9527 .child(
9528 StyledText::new(text_without_backticks.clone()).with_highlights(
9529 &cx.text_style(),
9530 code_ranges.iter().map(|range| {
9531 (
9532 range.clone(),
9533 HighlightStyle {
9534 color,
9535 ..Default::default()
9536 },
9537 )
9538 }),
9539 ),
9540 )
9541 .child(
9542 IconButton::new(("copy-block", cx.block_id), IconName::Copy)
9543 .icon_color(Color::Muted)
9544 .size(ButtonSize::Compact)
9545 .style(ButtonStyle::Transparent)
9546 .visible_on_hover(group_id)
9547 .on_click(cx.listener({
9548 let message = diagnostic.message.clone();
9549 move |_, _, cx| cx.write_to_clipboard(ClipboardItem::new(message.clone()))
9550 }))
9551 .tooltip(|cx| Tooltip::text("Copy diagnostic message", cx)),
9552 )
9553 .into_any_element()
9554 })
9555}
9556
9557pub fn highlight_diagnostic_message(diagnostic: &Diagnostic) -> (SharedString, Vec<Range<usize>>) {
9558 let mut text_without_backticks = String::new();
9559 let mut code_ranges = Vec::new();
9560
9561 if let Some(source) = &diagnostic.source {
9562 text_without_backticks.push_str(&source);
9563 code_ranges.push(0..source.len());
9564 text_without_backticks.push_str(": ");
9565 }
9566
9567 let mut prev_offset = 0;
9568 let mut in_code_block = false;
9569 for (ix, _) in diagnostic
9570 .message
9571 .match_indices('`')
9572 .chain([(diagnostic.message.len(), "")])
9573 {
9574 let prev_len = text_without_backticks.len();
9575 text_without_backticks.push_str(&diagnostic.message[prev_offset..ix]);
9576 prev_offset = ix + 1;
9577 if in_code_block {
9578 code_ranges.push(prev_len..text_without_backticks.len());
9579 in_code_block = false;
9580 } else {
9581 in_code_block = true;
9582 }
9583 }
9584
9585 (text_without_backticks.into(), code_ranges)
9586}
9587
9588fn diagnostic_style(severity: DiagnosticSeverity, valid: bool, colors: &StatusColors) -> Hsla {
9589 match (severity, valid) {
9590 (DiagnosticSeverity::ERROR, true) => colors.error,
9591 (DiagnosticSeverity::ERROR, false) => colors.error,
9592 (DiagnosticSeverity::WARNING, true) => colors.warning,
9593 (DiagnosticSeverity::WARNING, false) => colors.warning,
9594 (DiagnosticSeverity::INFORMATION, true) => colors.info,
9595 (DiagnosticSeverity::INFORMATION, false) => colors.info,
9596 (DiagnosticSeverity::HINT, true) => colors.info,
9597 (DiagnosticSeverity::HINT, false) => colors.info,
9598 _ => colors.ignored,
9599 }
9600}
9601
9602pub fn styled_runs_for_code_label<'a>(
9603 label: &'a CodeLabel,
9604 syntax_theme: &'a theme::SyntaxTheme,
9605) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
9606 let fade_out = HighlightStyle {
9607 fade_out: Some(0.35),
9608 ..Default::default()
9609 };
9610
9611 let mut prev_end = label.filter_range.end;
9612 label
9613 .runs
9614 .iter()
9615 .enumerate()
9616 .flat_map(move |(ix, (range, highlight_id))| {
9617 let style = if let Some(style) = highlight_id.style(syntax_theme) {
9618 style
9619 } else {
9620 return Default::default();
9621 };
9622 let mut muted_style = style;
9623 muted_style.highlight(fade_out);
9624
9625 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
9626 if range.start >= label.filter_range.end {
9627 if range.start > prev_end {
9628 runs.push((prev_end..range.start, fade_out));
9629 }
9630 runs.push((range.clone(), muted_style));
9631 } else if range.end <= label.filter_range.end {
9632 runs.push((range.clone(), style));
9633 } else {
9634 runs.push((range.start..label.filter_range.end, style));
9635 runs.push((label.filter_range.end..range.end, muted_style));
9636 }
9637 prev_end = cmp::max(prev_end, range.end);
9638
9639 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
9640 runs.push((prev_end..label.text.len(), fade_out));
9641 }
9642
9643 runs
9644 })
9645}
9646
9647pub(crate) fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
9648 let mut index = 0;
9649 let mut codepoints = text.char_indices().peekable();
9650
9651 std::iter::from_fn(move || {
9652 let start_index = index;
9653 while let Some((new_index, codepoint)) = codepoints.next() {
9654 index = new_index + codepoint.len_utf8();
9655 let current_upper = codepoint.is_uppercase();
9656 let next_upper = codepoints
9657 .peek()
9658 .map(|(_, c)| c.is_uppercase())
9659 .unwrap_or(false);
9660
9661 if !current_upper && next_upper {
9662 return Some(&text[start_index..index]);
9663 }
9664 }
9665
9666 index = text.len();
9667 if start_index < text.len() {
9668 return Some(&text[start_index..]);
9669 }
9670 None
9671 })
9672 .flat_map(|word| word.split_inclusive('_'))
9673 .flat_map(|word| word.split_inclusive('-'))
9674}
9675
9676trait RangeToAnchorExt {
9677 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
9678}
9679
9680impl<T: ToOffset> RangeToAnchorExt for Range<T> {
9681 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
9682 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
9683 }
9684}