1mod blink_manager;
2pub mod display_map;
3mod element;
4
5mod git;
6mod highlight_matching_bracket;
7mod hover_popover;
8pub mod items;
9mod link_go_to_definition;
10mod mouse_context_menu;
11pub mod movement;
12mod multi_buffer;
13mod persistence;
14pub mod scroll;
15pub mod selections_collection;
16
17#[cfg(test)]
18mod editor_tests;
19#[cfg(any(test, feature = "test-support"))]
20pub mod test;
21
22use aho_corasick::AhoCorasick;
23use anyhow::{anyhow, Result};
24use blink_manager::BlinkManager;
25use clock::ReplicaId;
26use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
27use copilot::Copilot;
28pub use display_map::DisplayPoint;
29use display_map::*;
30pub use element::*;
31use futures::FutureExt;
32use fuzzy::{StringMatch, StringMatchCandidate};
33use gpui::{
34 actions,
35 color::Color,
36 elements::*,
37 executor,
38 fonts::{self, HighlightStyle, TextStyle},
39 geometry::vector::Vector2F,
40 impl_actions, impl_internal_actions,
41 keymap_matcher::KeymapContext,
42 platform::{CursorStyle, MouseButton},
43 serde_json::{self, json},
44 AnyViewHandle, AppContext, AsyncAppContext, ClipboardItem, Element, ElementBox, Entity,
45 ModelHandle, RenderContext, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle,
46};
47use highlight_matching_bracket::refresh_matching_bracket_highlights;
48use hover_popover::{hide_hover, HideHover, HoverState};
49pub use items::MAX_TAB_TITLE_LEN;
50use itertools::Itertools;
51pub use language::{char_kind, CharKind};
52use language::{
53 AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel, Completion, CursorShape,
54 Diagnostic, DiagnosticSeverity, IndentKind, IndentSize, Language, OffsetRangeExt, OffsetUtf16,
55 Point, Rope, Selection, SelectionGoal, TransactionId,
56};
57use link_go_to_definition::{
58 hide_link_definition, show_link_definition, LinkDefinitionKind, LinkGoToDefinitionState,
59};
60pub use multi_buffer::{
61 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
62 ToPoint,
63};
64use multi_buffer::{MultiBufferChunks, ToOffsetUtf16};
65use ordered_float::OrderedFloat;
66use project::{FormatTrigger, Location, LocationLink, Project, ProjectPath, ProjectTransaction};
67use scroll::{
68 autoscroll::Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide,
69};
70use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection};
71use serde::{Deserialize, Serialize};
72use settings::Settings;
73use smallvec::SmallVec;
74use snippet::Snippet;
75use std::{
76 any::TypeId,
77 borrow::Cow,
78 cmp::{self, Ordering, Reverse},
79 mem,
80 num::NonZeroU32,
81 ops::{Deref, DerefMut, Range},
82 path::Path,
83 sync::Arc,
84 time::{Duration, Instant},
85};
86pub use sum_tree::Bias;
87use theme::{DiagnosticStyle, Theme};
88use util::{post_inc, RangeExt, ResultExt, TryFutureExt};
89use workspace::{ItemNavHistory, ViewId, Workspace, WorkspaceId};
90
91use crate::git::diff_hunk_to_display;
92
93const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
94const MAX_LINE_LEN: usize = 1024;
95const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
96const MAX_SELECTION_HISTORY_LEN: usize = 1024;
97const COPILOT_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
98
99pub const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
100
101#[derive(Clone, Deserialize, PartialEq, Default)]
102pub struct SelectNext {
103 #[serde(default)]
104 pub replace_newest: bool,
105}
106
107#[derive(Clone, PartialEq)]
108pub struct Select(pub SelectPhase);
109
110#[derive(Clone, Debug, PartialEq)]
111pub struct Jump {
112 path: ProjectPath,
113 position: Point,
114 anchor: language::Anchor,
115}
116
117#[derive(Clone, Deserialize, PartialEq)]
118pub struct SelectToBeginningOfLine {
119 #[serde(default)]
120 stop_at_soft_wraps: bool,
121}
122
123#[derive(Clone, Default, Deserialize, PartialEq)]
124pub struct MovePageUp {
125 #[serde(default)]
126 center_cursor: bool,
127}
128
129#[derive(Clone, Default, Deserialize, PartialEq)]
130pub struct MovePageDown {
131 #[serde(default)]
132 center_cursor: bool,
133}
134
135#[derive(Clone, Deserialize, PartialEq)]
136pub struct SelectToEndOfLine {
137 #[serde(default)]
138 stop_at_soft_wraps: bool,
139}
140
141#[derive(Clone, Deserialize, PartialEq)]
142pub struct ToggleCodeActions {
143 #[serde(default)]
144 pub deployed_from_indicator: bool,
145}
146
147#[derive(Clone, Default, Deserialize, PartialEq)]
148pub struct ConfirmCompletion {
149 #[serde(default)]
150 pub item_ix: Option<usize>,
151}
152
153#[derive(Clone, Default, Deserialize, PartialEq)]
154pub struct ConfirmCodeAction {
155 #[serde(default)]
156 pub item_ix: Option<usize>,
157}
158
159#[derive(Clone, Default, Deserialize, PartialEq)]
160pub struct ToggleComments {
161 #[serde(default)]
162 pub advance_downwards: bool,
163}
164
165#[derive(Clone, Default, Deserialize, PartialEq)]
166pub struct FoldAt {
167 pub buffer_row: u32,
168}
169
170#[derive(Clone, Default, Deserialize, PartialEq)]
171pub struct UnfoldAt {
172 pub buffer_row: u32,
173}
174
175#[derive(Clone, Default, Deserialize, PartialEq)]
176pub struct GutterHover {
177 pub hovered: bool,
178}
179
180actions!(
181 editor,
182 [
183 Cancel,
184 Backspace,
185 Delete,
186 Newline,
187 NewlineBelow,
188 GoToDiagnostic,
189 GoToPrevDiagnostic,
190 GoToHunk,
191 GoToPrevHunk,
192 Indent,
193 Outdent,
194 DeleteLine,
195 DeleteToPreviousWordStart,
196 DeleteToPreviousSubwordStart,
197 DeleteToNextWordEnd,
198 DeleteToNextSubwordEnd,
199 DeleteToBeginningOfLine,
200 DeleteToEndOfLine,
201 CutToEndOfLine,
202 DuplicateLine,
203 MoveLineUp,
204 MoveLineDown,
205 Transpose,
206 Cut,
207 Copy,
208 Paste,
209 Undo,
210 Redo,
211 MoveUp,
212 PageUp,
213 MoveDown,
214 PageDown,
215 MoveLeft,
216 MoveRight,
217 MoveToPreviousWordStart,
218 MoveToPreviousSubwordStart,
219 MoveToNextWordEnd,
220 MoveToNextSubwordEnd,
221 MoveToBeginningOfLine,
222 MoveToEndOfLine,
223 MoveToBeginning,
224 MoveToEnd,
225 SelectUp,
226 SelectDown,
227 SelectLeft,
228 SelectRight,
229 SelectToPreviousWordStart,
230 SelectToPreviousSubwordStart,
231 SelectToNextWordEnd,
232 SelectToNextSubwordEnd,
233 SelectToBeginning,
234 SelectToEnd,
235 SelectAll,
236 SelectLine,
237 SplitSelectionIntoLines,
238 AddSelectionAbove,
239 AddSelectionBelow,
240 Tab,
241 TabPrev,
242 ShowCharacterPalette,
243 SelectLargerSyntaxNode,
244 SelectSmallerSyntaxNode,
245 GoToDefinition,
246 GoToTypeDefinition,
247 MoveToEnclosingBracket,
248 UndoSelection,
249 RedoSelection,
250 FindAllReferences,
251 Rename,
252 ConfirmRename,
253 Fold,
254 UnfoldLines,
255 FoldSelectedRanges,
256 ShowCompletions,
257 OpenExcerpts,
258 RestartLanguageServer,
259 Hover,
260 Format,
261 ToggleSoftWrap,
262 RevealInFinder,
263 CopyPath,
264 CopyRelativePath,
265 CopyHighlightJson
266 ]
267);
268
269impl_actions!(
270 editor,
271 [
272 SelectNext,
273 SelectToBeginningOfLine,
274 SelectToEndOfLine,
275 ToggleCodeActions,
276 MovePageUp,
277 MovePageDown,
278 ConfirmCompletion,
279 ConfirmCodeAction,
280 ToggleComments,
281 FoldAt,
282 UnfoldAt,
283 GutterHover
284 ]
285);
286
287impl_internal_actions!(editor, [Select, Jump]);
288
289enum DocumentHighlightRead {}
290enum DocumentHighlightWrite {}
291enum InputComposition {}
292
293#[derive(Copy, Clone, PartialEq, Eq)]
294pub enum Direction {
295 Prev,
296 Next,
297}
298
299pub fn init(cx: &mut AppContext) {
300 cx.add_action(Editor::new_file);
301 cx.add_action(Editor::select);
302 cx.add_action(Editor::cancel);
303 cx.add_action(Editor::newline);
304 cx.add_action(Editor::newline_below);
305 cx.add_action(Editor::backspace);
306 cx.add_action(Editor::delete);
307 cx.add_action(Editor::tab);
308 cx.add_action(Editor::tab_prev);
309 cx.add_action(Editor::indent);
310 cx.add_action(Editor::outdent);
311 cx.add_action(Editor::delete_line);
312 cx.add_action(Editor::delete_to_previous_word_start);
313 cx.add_action(Editor::delete_to_previous_subword_start);
314 cx.add_action(Editor::delete_to_next_word_end);
315 cx.add_action(Editor::delete_to_next_subword_end);
316 cx.add_action(Editor::delete_to_beginning_of_line);
317 cx.add_action(Editor::delete_to_end_of_line);
318 cx.add_action(Editor::cut_to_end_of_line);
319 cx.add_action(Editor::duplicate_line);
320 cx.add_action(Editor::move_line_up);
321 cx.add_action(Editor::move_line_down);
322 cx.add_action(Editor::transpose);
323 cx.add_action(Editor::cut);
324 cx.add_action(Editor::copy);
325 cx.add_action(Editor::paste);
326 cx.add_action(Editor::undo);
327 cx.add_action(Editor::redo);
328 cx.add_action(Editor::move_up);
329 cx.add_action(Editor::move_page_up);
330 cx.add_action(Editor::move_down);
331 cx.add_action(Editor::move_page_down);
332 cx.add_action(Editor::next_screen);
333 cx.add_action(Editor::move_left);
334 cx.add_action(Editor::move_right);
335 cx.add_action(Editor::move_to_previous_word_start);
336 cx.add_action(Editor::move_to_previous_subword_start);
337 cx.add_action(Editor::move_to_next_word_end);
338 cx.add_action(Editor::move_to_next_subword_end);
339 cx.add_action(Editor::move_to_beginning_of_line);
340 cx.add_action(Editor::move_to_end_of_line);
341 cx.add_action(Editor::move_to_beginning);
342 cx.add_action(Editor::move_to_end);
343 cx.add_action(Editor::select_up);
344 cx.add_action(Editor::select_down);
345 cx.add_action(Editor::select_left);
346 cx.add_action(Editor::select_right);
347 cx.add_action(Editor::select_to_previous_word_start);
348 cx.add_action(Editor::select_to_previous_subword_start);
349 cx.add_action(Editor::select_to_next_word_end);
350 cx.add_action(Editor::select_to_next_subword_end);
351 cx.add_action(Editor::select_to_beginning_of_line);
352 cx.add_action(Editor::select_to_end_of_line);
353 cx.add_action(Editor::select_to_beginning);
354 cx.add_action(Editor::select_to_end);
355 cx.add_action(Editor::select_all);
356 cx.add_action(Editor::select_line);
357 cx.add_action(Editor::split_selection_into_lines);
358 cx.add_action(Editor::add_selection_above);
359 cx.add_action(Editor::add_selection_below);
360 cx.add_action(Editor::select_next);
361 cx.add_action(Editor::toggle_comments);
362 cx.add_action(Editor::select_larger_syntax_node);
363 cx.add_action(Editor::select_smaller_syntax_node);
364 cx.add_action(Editor::move_to_enclosing_bracket);
365 cx.add_action(Editor::undo_selection);
366 cx.add_action(Editor::redo_selection);
367 cx.add_action(Editor::go_to_diagnostic);
368 cx.add_action(Editor::go_to_prev_diagnostic);
369 cx.add_action(Editor::go_to_hunk);
370 cx.add_action(Editor::go_to_prev_hunk);
371 cx.add_action(Editor::go_to_definition);
372 cx.add_action(Editor::go_to_type_definition);
373 cx.add_action(Editor::fold);
374 cx.add_action(Editor::fold_at);
375 cx.add_action(Editor::unfold_lines);
376 cx.add_action(Editor::unfold_at);
377 cx.add_action(Editor::gutter_hover);
378 cx.add_action(Editor::fold_selected_ranges);
379 cx.add_action(Editor::show_completions);
380 cx.add_action(Editor::toggle_code_actions);
381 cx.add_action(Editor::open_excerpts);
382 cx.add_action(Editor::jump);
383 cx.add_action(Editor::toggle_soft_wrap);
384 cx.add_action(Editor::reveal_in_finder);
385 cx.add_action(Editor::copy_path);
386 cx.add_action(Editor::copy_relative_path);
387 cx.add_action(Editor::copy_highlight_json);
388 cx.add_async_action(Editor::format);
389 cx.add_action(Editor::restart_language_server);
390 cx.add_action(Editor::show_character_palette);
391 cx.add_async_action(Editor::confirm_completion);
392 cx.add_async_action(Editor::confirm_code_action);
393 cx.add_async_action(Editor::rename);
394 cx.add_async_action(Editor::confirm_rename);
395 cx.add_async_action(Editor::find_all_references);
396 cx.add_action(Editor::next_copilot_suggestion);
397 cx.add_action(Editor::previous_copilot_suggestion);
398 cx.add_action(Editor::copilot_suggest);
399
400 hover_popover::init(cx);
401 link_go_to_definition::init(cx);
402 mouse_context_menu::init(cx);
403 scroll::actions::init(cx);
404
405 workspace::register_project_item::<Editor>(cx);
406 workspace::register_followable_item::<Editor>(cx);
407 workspace::register_deserializable_item::<Editor>(cx);
408}
409
410trait InvalidationRegion {
411 fn ranges(&self) -> &[Range<Anchor>];
412}
413
414#[derive(Clone, Debug, PartialEq)]
415pub enum SelectPhase {
416 Begin {
417 position: DisplayPoint,
418 add: bool,
419 click_count: usize,
420 },
421 BeginColumnar {
422 position: DisplayPoint,
423 goal_column: u32,
424 },
425 Extend {
426 position: DisplayPoint,
427 click_count: usize,
428 },
429 Update {
430 position: DisplayPoint,
431 goal_column: u32,
432 scroll_position: Vector2F,
433 },
434 End,
435}
436
437#[derive(Clone, Debug)]
438pub enum SelectMode {
439 Character,
440 Word(Range<Anchor>),
441 Line(Range<Anchor>),
442 All,
443}
444
445#[derive(Copy, Clone, PartialEq, Eq, Debug)]
446pub enum EditorMode {
447 SingleLine,
448 AutoHeight { max_lines: usize },
449 Full,
450}
451
452#[derive(Clone)]
453pub enum SoftWrap {
454 None,
455 EditorWidth,
456 Column(u32),
457}
458
459#[derive(Clone)]
460pub struct EditorStyle {
461 pub text: TextStyle,
462 pub placeholder_text: Option<TextStyle>,
463 pub theme: theme::Editor,
464}
465
466type CompletionId = usize;
467
468type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
469type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
470
471pub struct Editor {
472 handle: WeakViewHandle<Self>,
473 buffer: ModelHandle<MultiBuffer>,
474 display_map: ModelHandle<DisplayMap>,
475 pub selections: SelectionsCollection,
476 pub scroll_manager: ScrollManager,
477 columnar_selection_tail: Option<Anchor>,
478 add_selections_state: Option<AddSelectionsState>,
479 select_next_state: Option<SelectNextState>,
480 selection_history: SelectionHistory,
481 autoclose_regions: Vec<AutocloseRegion>,
482 snippet_stack: InvalidationStack<SnippetState>,
483 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
484 ime_transaction: Option<TransactionId>,
485 active_diagnostics: Option<ActiveDiagnosticGroup>,
486 soft_wrap_mode_override: Option<settings::SoftWrap>,
487 get_field_editor_theme: Option<Arc<GetFieldEditorTheme>>,
488 override_text_style: Option<Box<OverrideTextStyle>>,
489 project: Option<ModelHandle<Project>>,
490 focused: bool,
491 blink_manager: ModelHandle<BlinkManager>,
492 show_local_selections: bool,
493 mode: EditorMode,
494 placeholder_text: Option<Arc<str>>,
495 highlighted_rows: Option<Range<u32>>,
496 #[allow(clippy::type_complexity)]
497 background_highlights: BTreeMap<TypeId, (fn(&Theme) -> Color, Vec<Range<Anchor>>)>,
498 nav_history: Option<ItemNavHistory>,
499 context_menu: Option<ContextMenu>,
500 mouse_context_menu: ViewHandle<context_menu::ContextMenu>,
501 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
502 next_completion_id: CompletionId,
503 available_code_actions: Option<(ModelHandle<Buffer>, Arc<[CodeAction]>)>,
504 code_actions_task: Option<Task<()>>,
505 document_highlights_task: Option<Task<()>>,
506 pending_rename: Option<RenameState>,
507 searchable: bool,
508 cursor_shape: CursorShape,
509 workspace_id: Option<WorkspaceId>,
510 keymap_context_layers: BTreeMap<TypeId, KeymapContext>,
511 input_enabled: bool,
512 leader_replica_id: Option<u16>,
513 remote_id: Option<ViewId>,
514 hover_state: HoverState,
515 gutter_hovered: bool,
516 link_go_to_definition_state: LinkGoToDefinitionState,
517 copilot_state: CopilotState,
518 _subscriptions: Vec<Subscription>,
519}
520
521pub struct EditorSnapshot {
522 pub mode: EditorMode,
523 pub display_snapshot: DisplaySnapshot,
524 pub placeholder_text: Option<Arc<str>>,
525 is_focused: bool,
526 scroll_anchor: ScrollAnchor,
527 ongoing_scroll: OngoingScroll,
528}
529
530#[derive(Clone, Debug)]
531struct SelectionHistoryEntry {
532 selections: Arc<[Selection<Anchor>]>,
533 select_next_state: Option<SelectNextState>,
534 add_selections_state: Option<AddSelectionsState>,
535}
536
537enum SelectionHistoryMode {
538 Normal,
539 Undoing,
540 Redoing,
541}
542
543impl Default for SelectionHistoryMode {
544 fn default() -> Self {
545 Self::Normal
546 }
547}
548
549#[derive(Default)]
550struct SelectionHistory {
551 #[allow(clippy::type_complexity)]
552 selections_by_transaction:
553 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
554 mode: SelectionHistoryMode,
555 undo_stack: VecDeque<SelectionHistoryEntry>,
556 redo_stack: VecDeque<SelectionHistoryEntry>,
557}
558
559impl SelectionHistory {
560 fn insert_transaction(
561 &mut self,
562 transaction_id: TransactionId,
563 selections: Arc<[Selection<Anchor>]>,
564 ) {
565 self.selections_by_transaction
566 .insert(transaction_id, (selections, None));
567 }
568
569 #[allow(clippy::type_complexity)]
570 fn transaction(
571 &self,
572 transaction_id: TransactionId,
573 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
574 self.selections_by_transaction.get(&transaction_id)
575 }
576
577 #[allow(clippy::type_complexity)]
578 fn transaction_mut(
579 &mut self,
580 transaction_id: TransactionId,
581 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
582 self.selections_by_transaction.get_mut(&transaction_id)
583 }
584
585 fn push(&mut self, entry: SelectionHistoryEntry) {
586 if !entry.selections.is_empty() {
587 match self.mode {
588 SelectionHistoryMode::Normal => {
589 self.push_undo(entry);
590 self.redo_stack.clear();
591 }
592 SelectionHistoryMode::Undoing => self.push_redo(entry),
593 SelectionHistoryMode::Redoing => self.push_undo(entry),
594 }
595 }
596 }
597
598 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
599 if self
600 .undo_stack
601 .back()
602 .map_or(true, |e| e.selections != entry.selections)
603 {
604 self.undo_stack.push_back(entry);
605 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
606 self.undo_stack.pop_front();
607 }
608 }
609 }
610
611 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
612 if self
613 .redo_stack
614 .back()
615 .map_or(true, |e| e.selections != entry.selections)
616 {
617 self.redo_stack.push_back(entry);
618 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
619 self.redo_stack.pop_front();
620 }
621 }
622 }
623}
624
625#[derive(Clone, Debug)]
626struct AddSelectionsState {
627 above: bool,
628 stack: Vec<usize>,
629}
630
631#[derive(Clone, Debug)]
632struct SelectNextState {
633 query: AhoCorasick,
634 wordwise: bool,
635 done: bool,
636}
637
638#[derive(Debug)]
639struct AutocloseRegion {
640 selection_id: usize,
641 range: Range<Anchor>,
642 pair: BracketPair,
643}
644
645#[derive(Debug)]
646struct SnippetState {
647 ranges: Vec<Vec<Range<Anchor>>>,
648 active_index: usize,
649}
650
651pub struct RenameState {
652 pub range: Range<Anchor>,
653 pub old_name: Arc<str>,
654 pub editor: ViewHandle<Editor>,
655 block_id: BlockId,
656}
657
658struct InvalidationStack<T>(Vec<T>);
659
660enum ContextMenu {
661 Completions(CompletionsMenu),
662 CodeActions(CodeActionsMenu),
663}
664
665impl ContextMenu {
666 fn select_first(&mut self, cx: &mut ViewContext<Editor>) -> bool {
667 if self.visible() {
668 match self {
669 ContextMenu::Completions(menu) => menu.select_first(cx),
670 ContextMenu::CodeActions(menu) => menu.select_first(cx),
671 }
672 true
673 } else {
674 false
675 }
676 }
677
678 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) -> bool {
679 if self.visible() {
680 match self {
681 ContextMenu::Completions(menu) => menu.select_prev(cx),
682 ContextMenu::CodeActions(menu) => menu.select_prev(cx),
683 }
684 true
685 } else {
686 false
687 }
688 }
689
690 fn select_next(&mut self, cx: &mut ViewContext<Editor>) -> bool {
691 if self.visible() {
692 match self {
693 ContextMenu::Completions(menu) => menu.select_next(cx),
694 ContextMenu::CodeActions(menu) => menu.select_next(cx),
695 }
696 true
697 } else {
698 false
699 }
700 }
701
702 fn select_last(&mut self, cx: &mut ViewContext<Editor>) -> bool {
703 if self.visible() {
704 match self {
705 ContextMenu::Completions(menu) => menu.select_last(cx),
706 ContextMenu::CodeActions(menu) => menu.select_last(cx),
707 }
708 true
709 } else {
710 false
711 }
712 }
713
714 fn visible(&self) -> bool {
715 match self {
716 ContextMenu::Completions(menu) => menu.visible(),
717 ContextMenu::CodeActions(menu) => menu.visible(),
718 }
719 }
720
721 fn render(
722 &self,
723 cursor_position: DisplayPoint,
724 style: EditorStyle,
725 cx: &mut RenderContext<Editor>,
726 ) -> (DisplayPoint, ElementBox) {
727 match self {
728 ContextMenu::Completions(menu) => (cursor_position, menu.render(style, cx)),
729 ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, cx),
730 }
731 }
732}
733
734struct CompletionsMenu {
735 id: CompletionId,
736 initial_position: Anchor,
737 buffer: ModelHandle<Buffer>,
738 completions: Arc<[Completion]>,
739 match_candidates: Vec<StringMatchCandidate>,
740 matches: Arc<[StringMatch]>,
741 selected_item: usize,
742 list: UniformListState,
743}
744
745impl CompletionsMenu {
746 fn select_first(&mut self, cx: &mut ViewContext<Editor>) {
747 self.selected_item = 0;
748 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
749 cx.notify();
750 }
751
752 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
753 if self.selected_item > 0 {
754 self.selected_item -= 1;
755 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
756 }
757 cx.notify();
758 }
759
760 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
761 if self.selected_item + 1 < self.matches.len() {
762 self.selected_item += 1;
763 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
764 }
765 cx.notify();
766 }
767
768 fn select_last(&mut self, cx: &mut ViewContext<Editor>) {
769 self.selected_item = self.matches.len() - 1;
770 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
771 cx.notify();
772 }
773
774 fn visible(&self) -> bool {
775 !self.matches.is_empty()
776 }
777
778 fn render(&self, style: EditorStyle, cx: &mut RenderContext<Editor>) -> ElementBox {
779 enum CompletionTag {}
780
781 let completions = self.completions.clone();
782 let matches = self.matches.clone();
783 let selected_item = self.selected_item;
784 let container_style = style.autocomplete.container;
785 UniformList::new(
786 self.list.clone(),
787 matches.len(),
788 cx,
789 move |_, range, items, cx| {
790 let start_ix = range.start;
791 for (ix, mat) in matches[range].iter().enumerate() {
792 let completion = &completions[mat.candidate_id];
793 let item_ix = start_ix + ix;
794 items.push(
795 MouseEventHandler::<CompletionTag>::new(
796 mat.candidate_id,
797 cx,
798 |state, _| {
799 let item_style = if item_ix == selected_item {
800 style.autocomplete.selected_item
801 } else if state.hovered() {
802 style.autocomplete.hovered_item
803 } else {
804 style.autocomplete.item
805 };
806
807 Text::new(completion.label.text.clone(), style.text.clone())
808 .with_soft_wrap(false)
809 .with_highlights(combine_syntax_and_fuzzy_match_highlights(
810 &completion.label.text,
811 style.text.color.into(),
812 styled_runs_for_code_label(
813 &completion.label,
814 &style.syntax,
815 ),
816 &mat.positions,
817 ))
818 .contained()
819 .with_style(item_style)
820 .boxed()
821 },
822 )
823 .with_cursor_style(CursorStyle::PointingHand)
824 .on_down(MouseButton::Left, move |_, cx| {
825 cx.dispatch_action(ConfirmCompletion {
826 item_ix: Some(item_ix),
827 });
828 })
829 .boxed(),
830 );
831 }
832 },
833 )
834 .with_width_from_item(
835 self.matches
836 .iter()
837 .enumerate()
838 .max_by_key(|(_, mat)| {
839 self.completions[mat.candidate_id]
840 .label
841 .text
842 .chars()
843 .count()
844 })
845 .map(|(ix, _)| ix),
846 )
847 .contained()
848 .with_style(container_style)
849 .boxed()
850 }
851
852 pub async fn filter(&mut self, query: Option<&str>, executor: Arc<executor::Background>) {
853 let mut matches = if let Some(query) = query {
854 fuzzy::match_strings(
855 &self.match_candidates,
856 query,
857 query.chars().any(|c| c.is_uppercase()),
858 100,
859 &Default::default(),
860 executor,
861 )
862 .await
863 } else {
864 self.match_candidates
865 .iter()
866 .enumerate()
867 .map(|(candidate_id, candidate)| StringMatch {
868 candidate_id,
869 score: Default::default(),
870 positions: Default::default(),
871 string: candidate.string.clone(),
872 })
873 .collect()
874 };
875
876 //Remove all candidates where the query's start does not match the start of any word in the candidate
877 if let Some(query) = query {
878 if let Some(query_start) = query.chars().next() {
879 matches.retain(|string_match| {
880 split_words(&string_match.string).any(|word| {
881 //Check that the first codepoint of the word as lowercase matches the first
882 //codepoint of the query as lowercase
883 word.chars()
884 .flat_map(|codepoint| codepoint.to_lowercase())
885 .zip(query_start.to_lowercase())
886 .all(|(word_cp, query_cp)| word_cp == query_cp)
887 })
888 });
889 }
890 }
891
892 matches.sort_unstable_by_key(|mat| {
893 let completion = &self.completions[mat.candidate_id];
894 (
895 completion.lsp_completion.sort_text.as_ref(),
896 Reverse(OrderedFloat(mat.score)),
897 completion.sort_key(),
898 )
899 });
900
901 for mat in &mut matches {
902 let filter_start = self.completions[mat.candidate_id].label.filter_range.start;
903 for position in &mut mat.positions {
904 *position += filter_start;
905 }
906 }
907
908 self.matches = matches.into();
909 }
910}
911
912#[derive(Clone)]
913struct CodeActionsMenu {
914 actions: Arc<[CodeAction]>,
915 buffer: ModelHandle<Buffer>,
916 selected_item: usize,
917 list: UniformListState,
918 deployed_from_indicator: bool,
919}
920
921impl CodeActionsMenu {
922 fn select_first(&mut self, cx: &mut ViewContext<Editor>) {
923 self.selected_item = 0;
924 cx.notify()
925 }
926
927 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
928 if self.selected_item > 0 {
929 self.selected_item -= 1;
930 cx.notify()
931 }
932 }
933
934 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
935 if self.selected_item + 1 < self.actions.len() {
936 self.selected_item += 1;
937 cx.notify()
938 }
939 }
940
941 fn select_last(&mut self, cx: &mut ViewContext<Editor>) {
942 self.selected_item = self.actions.len() - 1;
943 cx.notify()
944 }
945
946 fn visible(&self) -> bool {
947 !self.actions.is_empty()
948 }
949
950 fn render(
951 &self,
952 mut cursor_position: DisplayPoint,
953 style: EditorStyle,
954 cx: &mut RenderContext<Editor>,
955 ) -> (DisplayPoint, ElementBox) {
956 enum ActionTag {}
957
958 let container_style = style.autocomplete.container;
959 let actions = self.actions.clone();
960 let selected_item = self.selected_item;
961 let element = UniformList::new(
962 self.list.clone(),
963 actions.len(),
964 cx,
965 move |_, range, items, cx| {
966 let start_ix = range.start;
967 for (ix, action) in actions[range].iter().enumerate() {
968 let item_ix = start_ix + ix;
969 items.push(
970 MouseEventHandler::<ActionTag>::new(item_ix, cx, |state, _| {
971 let item_style = if item_ix == selected_item {
972 style.autocomplete.selected_item
973 } else if state.hovered() {
974 style.autocomplete.hovered_item
975 } else {
976 style.autocomplete.item
977 };
978
979 Text::new(action.lsp_action.title.clone(), style.text.clone())
980 .with_soft_wrap(false)
981 .contained()
982 .with_style(item_style)
983 .boxed()
984 })
985 .with_cursor_style(CursorStyle::PointingHand)
986 .on_down(MouseButton::Left, move |_, cx| {
987 cx.dispatch_action(ConfirmCodeAction {
988 item_ix: Some(item_ix),
989 });
990 })
991 .boxed(),
992 );
993 }
994 },
995 )
996 .with_width_from_item(
997 self.actions
998 .iter()
999 .enumerate()
1000 .max_by_key(|(_, action)| action.lsp_action.title.chars().count())
1001 .map(|(ix, _)| ix),
1002 )
1003 .contained()
1004 .with_style(container_style)
1005 .boxed();
1006
1007 if self.deployed_from_indicator {
1008 *cursor_position.column_mut() = 0;
1009 }
1010
1011 (cursor_position, element)
1012 }
1013}
1014
1015pub struct CopilotState {
1016 excerpt_id: Option<ExcerptId>,
1017 pending_refresh: Task<Option<()>>,
1018 pending_cycling_refresh: Task<Option<()>>,
1019 cycled: bool,
1020 completions: Vec<copilot::Completion>,
1021 active_completion_index: usize,
1022}
1023
1024impl Default for CopilotState {
1025 fn default() -> Self {
1026 Self {
1027 excerpt_id: None,
1028 pending_cycling_refresh: Task::ready(Some(())),
1029 pending_refresh: Task::ready(Some(())),
1030 completions: Default::default(),
1031 active_completion_index: 0,
1032 cycled: false,
1033 }
1034 }
1035}
1036
1037impl CopilotState {
1038 fn text_for_active_completion(
1039 &self,
1040 cursor: Anchor,
1041 buffer: &MultiBufferSnapshot,
1042 ) -> Option<&str> {
1043 use language::ToOffset as _;
1044
1045 let completion = self.completions.get(self.active_completion_index)?;
1046 let excerpt_id = self.excerpt_id?;
1047 let completion_buffer = buffer.buffer_for_excerpt(excerpt_id)?;
1048 if excerpt_id != cursor.excerpt_id
1049 || !completion.range.start.is_valid(completion_buffer)
1050 || !completion.range.end.is_valid(completion_buffer)
1051 {
1052 return None;
1053 }
1054
1055 let mut completion_range = completion.range.to_offset(&completion_buffer);
1056 let prefix_len = Self::common_prefix(
1057 completion_buffer.chars_for_range(completion_range.clone()),
1058 completion.text.chars(),
1059 );
1060 completion_range.start += prefix_len;
1061 let suffix_len = Self::common_prefix(
1062 completion_buffer.reversed_chars_for_range(completion_range.clone()),
1063 completion.text[prefix_len..].chars().rev(),
1064 );
1065 completion_range.end = completion_range.end.saturating_sub(suffix_len);
1066
1067 if completion_range.is_empty()
1068 && completion_range.start == cursor.text_anchor.to_offset(&completion_buffer)
1069 {
1070 Some(&completion.text[prefix_len..completion.text.len() - suffix_len])
1071 } else {
1072 None
1073 }
1074 }
1075
1076 fn cycle_completions(&mut self, direction: Direction) {
1077 match direction {
1078 Direction::Prev => {
1079 self.active_completion_index = if self.active_completion_index == 0 {
1080 self.completions.len() - 1
1081 } else {
1082 self.active_completion_index - 1
1083 };
1084 }
1085 Direction::Next => {
1086 if self.completions.len() == 0 {
1087 self.active_completion_index = 0
1088 } else {
1089 self.active_completion_index =
1090 (self.active_completion_index + 1) % self.completions.len();
1091 }
1092 }
1093 }
1094 }
1095
1096 fn push_completion(&mut self, new_completion: copilot::Completion) {
1097 for completion in &self.completions {
1098 if *completion == new_completion {
1099 return;
1100 }
1101 }
1102 self.completions.push(new_completion);
1103 }
1104
1105 fn common_prefix<T1: Iterator<Item = char>, T2: Iterator<Item = char>>(a: T1, b: T2) -> usize {
1106 a.zip(b)
1107 .take_while(|(a, b)| a == b)
1108 .map(|(a, _)| a.len_utf8())
1109 .sum()
1110 }
1111}
1112
1113#[derive(Debug)]
1114struct ActiveDiagnosticGroup {
1115 primary_range: Range<Anchor>,
1116 primary_message: String,
1117 blocks: HashMap<BlockId, Diagnostic>,
1118 is_valid: bool,
1119}
1120
1121#[derive(Serialize, Deserialize)]
1122pub struct ClipboardSelection {
1123 pub len: usize,
1124 pub is_entire_line: bool,
1125 pub first_line_indent: u32,
1126}
1127
1128#[derive(Debug)]
1129pub struct NavigationData {
1130 cursor_anchor: Anchor,
1131 cursor_position: Point,
1132 scroll_anchor: ScrollAnchor,
1133 scroll_top_row: u32,
1134}
1135
1136pub struct EditorCreated(pub ViewHandle<Editor>);
1137
1138enum GotoDefinitionKind {
1139 Symbol,
1140 Type,
1141}
1142
1143impl Editor {
1144 pub fn single_line(
1145 field_editor_style: Option<Arc<GetFieldEditorTheme>>,
1146 cx: &mut ViewContext<Self>,
1147 ) -> Self {
1148 let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx));
1149 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
1150 Self::new(EditorMode::SingleLine, buffer, None, field_editor_style, cx)
1151 }
1152
1153 pub fn multi_line(
1154 field_editor_style: Option<Arc<GetFieldEditorTheme>>,
1155 cx: &mut ViewContext<Self>,
1156 ) -> Self {
1157 let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx));
1158 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
1159 Self::new(EditorMode::Full, buffer, None, field_editor_style, cx)
1160 }
1161
1162 pub fn auto_height(
1163 max_lines: usize,
1164 field_editor_style: Option<Arc<GetFieldEditorTheme>>,
1165 cx: &mut ViewContext<Self>,
1166 ) -> Self {
1167 let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx));
1168 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
1169 Self::new(
1170 EditorMode::AutoHeight { max_lines },
1171 buffer,
1172 None,
1173 field_editor_style,
1174 cx,
1175 )
1176 }
1177
1178 pub fn for_buffer(
1179 buffer: ModelHandle<Buffer>,
1180 project: Option<ModelHandle<Project>>,
1181 cx: &mut ViewContext<Self>,
1182 ) -> Self {
1183 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
1184 Self::new(EditorMode::Full, buffer, project, None, cx)
1185 }
1186
1187 pub fn for_multibuffer(
1188 buffer: ModelHandle<MultiBuffer>,
1189 project: Option<ModelHandle<Project>>,
1190 cx: &mut ViewContext<Self>,
1191 ) -> Self {
1192 Self::new(EditorMode::Full, buffer, project, None, cx)
1193 }
1194
1195 pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
1196 let mut clone = Self::new(
1197 self.mode,
1198 self.buffer.clone(),
1199 self.project.clone(),
1200 self.get_field_editor_theme.clone(),
1201 cx,
1202 );
1203 self.display_map.update(cx, |display_map, cx| {
1204 let snapshot = display_map.snapshot(cx);
1205 clone.display_map.update(cx, |display_map, cx| {
1206 display_map.set_state(&snapshot, cx);
1207 });
1208 });
1209 clone.selections.clone_state(&self.selections);
1210 clone.scroll_manager.clone_state(&self.scroll_manager);
1211 clone.searchable = self.searchable;
1212 clone
1213 }
1214
1215 fn new(
1216 mode: EditorMode,
1217 buffer: ModelHandle<MultiBuffer>,
1218 project: Option<ModelHandle<Project>>,
1219 get_field_editor_theme: Option<Arc<GetFieldEditorTheme>>,
1220 cx: &mut ViewContext<Self>,
1221 ) -> Self {
1222 let display_map = cx.add_model(|cx| {
1223 let settings = cx.global::<Settings>();
1224 let style = build_style(&*settings, get_field_editor_theme.as_deref(), None, cx);
1225 DisplayMap::new(
1226 buffer.clone(),
1227 style.text.font_id,
1228 style.text.font_size,
1229 None,
1230 2,
1231 1,
1232 cx,
1233 )
1234 });
1235
1236 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1237
1238 let blink_manager = cx.add_model(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1239
1240 let soft_wrap_mode_override =
1241 (mode == EditorMode::SingleLine).then(|| settings::SoftWrap::None);
1242 let mut this = Self {
1243 handle: cx.weak_handle(),
1244 buffer: buffer.clone(),
1245 display_map: display_map.clone(),
1246 selections,
1247 scroll_manager: ScrollManager::new(),
1248 columnar_selection_tail: None,
1249 add_selections_state: None,
1250 select_next_state: None,
1251 selection_history: Default::default(),
1252 autoclose_regions: Default::default(),
1253 snippet_stack: Default::default(),
1254 select_larger_syntax_node_stack: Vec::new(),
1255 ime_transaction: Default::default(),
1256 active_diagnostics: None,
1257 soft_wrap_mode_override,
1258 get_field_editor_theme,
1259 project,
1260 focused: false,
1261 blink_manager: blink_manager.clone(),
1262 show_local_selections: true,
1263 mode,
1264 placeholder_text: None,
1265 highlighted_rows: None,
1266 background_highlights: Default::default(),
1267 nav_history: None,
1268 context_menu: None,
1269 mouse_context_menu: cx.add_view(context_menu::ContextMenu::new),
1270 completion_tasks: Default::default(),
1271 next_completion_id: 0,
1272 available_code_actions: Default::default(),
1273 code_actions_task: Default::default(),
1274 document_highlights_task: Default::default(),
1275 pending_rename: Default::default(),
1276 searchable: true,
1277 override_text_style: None,
1278 cursor_shape: Default::default(),
1279 workspace_id: None,
1280 keymap_context_layers: Default::default(),
1281 input_enabled: true,
1282 leader_replica_id: None,
1283 remote_id: None,
1284 hover_state: Default::default(),
1285 link_go_to_definition_state: Default::default(),
1286 copilot_state: Default::default(),
1287 gutter_hovered: false,
1288 _subscriptions: vec![
1289 cx.observe(&buffer, Self::on_buffer_changed),
1290 cx.subscribe(&buffer, Self::on_buffer_event),
1291 cx.observe(&display_map, Self::on_display_map_changed),
1292 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1293 cx.observe_global::<Settings, _>(Self::settings_changed),
1294 ],
1295 };
1296 this.end_selection(cx);
1297 this.scroll_manager.show_scrollbar(cx);
1298
1299 let editor_created_event = EditorCreated(cx.handle());
1300 cx.emit_global(editor_created_event);
1301
1302 if mode == EditorMode::Full {
1303 let should_auto_hide_scrollbars = cx.platform().should_auto_hide_scrollbars();
1304 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1305 }
1306
1307 this.report_event("open editor", cx);
1308 this
1309 }
1310
1311 pub fn new_file(
1312 workspace: &mut Workspace,
1313 _: &workspace::NewFile,
1314 cx: &mut ViewContext<Workspace>,
1315 ) {
1316 let project = workspace.project().clone();
1317 if project.read(cx).is_remote() {
1318 cx.propagate_action();
1319 } else if let Some(buffer) = project
1320 .update(cx, |project, cx| project.create_buffer("", None, cx))
1321 .log_err()
1322 {
1323 workspace.add_item(
1324 Box::new(cx.add_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx))),
1325 cx,
1326 );
1327 }
1328 }
1329
1330 pub fn replica_id(&self, cx: &AppContext) -> ReplicaId {
1331 self.buffer.read(cx).replica_id()
1332 }
1333
1334 pub fn leader_replica_id(&self) -> Option<ReplicaId> {
1335 self.leader_replica_id
1336 }
1337
1338 pub fn buffer(&self) -> &ModelHandle<MultiBuffer> {
1339 &self.buffer
1340 }
1341
1342 pub fn title<'a>(&self, cx: &'a AppContext) -> Cow<'a, str> {
1343 self.buffer().read(cx).title(cx)
1344 }
1345
1346 pub fn snapshot(&mut self, cx: &mut AppContext) -> EditorSnapshot {
1347 EditorSnapshot {
1348 mode: self.mode,
1349 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1350 scroll_anchor: self.scroll_manager.anchor(),
1351 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
1352 placeholder_text: self.placeholder_text.clone(),
1353 is_focused: self
1354 .handle
1355 .upgrade(cx)
1356 .map_or(false, |handle| handle.is_focused(cx)),
1357 }
1358 }
1359
1360 pub fn language_at<'a, T: ToOffset>(
1361 &self,
1362 point: T,
1363 cx: &'a AppContext,
1364 ) -> Option<Arc<Language>> {
1365 self.buffer.read(cx).language_at(point, cx)
1366 }
1367
1368 pub fn active_excerpt(
1369 &self,
1370 cx: &AppContext,
1371 ) -> Option<(ExcerptId, ModelHandle<Buffer>, Range<text::Anchor>)> {
1372 self.buffer
1373 .read(cx)
1374 .excerpt_containing(self.selections.newest_anchor().head(), cx)
1375 }
1376
1377 fn style(&self, cx: &AppContext) -> EditorStyle {
1378 build_style(
1379 cx.global::<Settings>(),
1380 self.get_field_editor_theme.as_deref(),
1381 self.override_text_style.as_deref(),
1382 cx,
1383 )
1384 }
1385
1386 pub fn mode(&self) -> EditorMode {
1387 self.mode
1388 }
1389
1390 pub fn set_placeholder_text(
1391 &mut self,
1392 placeholder_text: impl Into<Arc<str>>,
1393 cx: &mut ViewContext<Self>,
1394 ) {
1395 self.placeholder_text = Some(placeholder_text.into());
1396 cx.notify();
1397 }
1398
1399 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
1400 self.cursor_shape = cursor_shape;
1401 cx.notify();
1402 }
1403
1404 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut ViewContext<Self>) {
1405 if self.display_map.read(cx).clip_at_line_ends != clip {
1406 self.display_map
1407 .update(cx, |map, _| map.clip_at_line_ends = clip);
1408 }
1409 }
1410
1411 pub fn set_keymap_context_layer<Tag: 'static>(&mut self, context: KeymapContext) {
1412 self.keymap_context_layers
1413 .insert(TypeId::of::<Tag>(), context);
1414 }
1415
1416 pub fn remove_keymap_context_layer<Tag: 'static>(&mut self) {
1417 self.keymap_context_layers.remove(&TypeId::of::<Tag>());
1418 }
1419
1420 pub fn set_input_enabled(&mut self, input_enabled: bool) {
1421 self.input_enabled = input_enabled;
1422 }
1423
1424 fn selections_did_change(
1425 &mut self,
1426 local: bool,
1427 old_cursor_position: &Anchor,
1428 cx: &mut ViewContext<Self>,
1429 ) {
1430 if self.focused && self.leader_replica_id.is_none() {
1431 self.buffer.update(cx, |buffer, cx| {
1432 buffer.set_active_selections(
1433 &self.selections.disjoint_anchors(),
1434 self.selections.line_mode,
1435 self.cursor_shape,
1436 cx,
1437 )
1438 });
1439 }
1440
1441 let display_map = self
1442 .display_map
1443 .update(cx, |display_map, cx| display_map.snapshot(cx));
1444 let buffer = &display_map.buffer_snapshot;
1445 self.add_selections_state = None;
1446 self.select_next_state = None;
1447 self.select_larger_syntax_node_stack.clear();
1448 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
1449 self.snippet_stack
1450 .invalidate(&self.selections.disjoint_anchors(), buffer);
1451 self.take_rename(false, cx);
1452
1453 let new_cursor_position = self.selections.newest_anchor().head();
1454
1455 self.push_to_nav_history(
1456 old_cursor_position.clone(),
1457 Some(new_cursor_position.to_point(buffer)),
1458 cx,
1459 );
1460
1461 if local {
1462 let new_cursor_position = self.selections.newest_anchor().head();
1463 let completion_menu = match self.context_menu.as_mut() {
1464 Some(ContextMenu::Completions(menu)) => Some(menu),
1465 _ => {
1466 self.context_menu.take();
1467 None
1468 }
1469 };
1470
1471 if let Some(completion_menu) = completion_menu {
1472 let cursor_position = new_cursor_position.to_offset(buffer);
1473 let (word_range, kind) =
1474 buffer.surrounding_word(completion_menu.initial_position.clone());
1475 if kind == Some(CharKind::Word)
1476 && word_range.to_inclusive().contains(&cursor_position)
1477 {
1478 let query = Self::completion_query(buffer, cursor_position);
1479 cx.background()
1480 .block(completion_menu.filter(query.as_deref(), cx.background().clone()));
1481 self.show_completions(&ShowCompletions, cx);
1482 } else {
1483 self.hide_context_menu(cx);
1484 }
1485 }
1486
1487 hide_hover(self, &HideHover, cx);
1488
1489 if old_cursor_position.to_display_point(&display_map).row()
1490 != new_cursor_position.to_display_point(&display_map).row()
1491 {
1492 self.available_code_actions.take();
1493 }
1494 self.refresh_code_actions(cx);
1495 self.refresh_document_highlights(cx);
1496 refresh_matching_bracket_highlights(self, cx);
1497 self.hide_copilot_suggestion(cx);
1498 }
1499
1500 self.blink_manager.update(cx, BlinkManager::pause_blinking);
1501 cx.emit(Event::SelectionsChanged { local });
1502 cx.notify();
1503 }
1504
1505 pub fn change_selections<R>(
1506 &mut self,
1507 autoscroll: Option<Autoscroll>,
1508 cx: &mut ViewContext<Self>,
1509 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
1510 ) -> R {
1511 let old_cursor_position = self.selections.newest_anchor().head();
1512 self.push_to_selection_history();
1513
1514 let (changed, result) = self.selections.change_with(cx, change);
1515
1516 if changed {
1517 if let Some(autoscroll) = autoscroll {
1518 self.request_autoscroll(autoscroll, cx);
1519 }
1520 self.selections_did_change(true, &old_cursor_position, cx);
1521 }
1522
1523 result
1524 }
1525
1526 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
1527 where
1528 I: IntoIterator<Item = (Range<S>, T)>,
1529 S: ToOffset,
1530 T: Into<Arc<str>>,
1531 {
1532 self.buffer
1533 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
1534 }
1535
1536 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
1537 where
1538 I: IntoIterator<Item = (Range<S>, T)>,
1539 S: ToOffset,
1540 T: Into<Arc<str>>,
1541 {
1542 self.buffer.update(cx, |buffer, cx| {
1543 buffer.edit(edits, Some(AutoindentMode::EachLine), cx)
1544 });
1545 }
1546
1547 fn select(&mut self, Select(phase): &Select, cx: &mut ViewContext<Self>) {
1548 self.hide_context_menu(cx);
1549
1550 match phase {
1551 SelectPhase::Begin {
1552 position,
1553 add,
1554 click_count,
1555 } => self.begin_selection(*position, *add, *click_count, cx),
1556 SelectPhase::BeginColumnar {
1557 position,
1558 goal_column,
1559 } => self.begin_columnar_selection(*position, *goal_column, cx),
1560 SelectPhase::Extend {
1561 position,
1562 click_count,
1563 } => self.extend_selection(*position, *click_count, cx),
1564 SelectPhase::Update {
1565 position,
1566 goal_column,
1567 scroll_position,
1568 } => self.update_selection(*position, *goal_column, *scroll_position, cx),
1569 SelectPhase::End => self.end_selection(cx),
1570 }
1571 }
1572
1573 fn extend_selection(
1574 &mut self,
1575 position: DisplayPoint,
1576 click_count: usize,
1577 cx: &mut ViewContext<Self>,
1578 ) {
1579 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1580 let tail = self.selections.newest::<usize>(cx).tail();
1581 self.begin_selection(position, false, click_count, cx);
1582
1583 let position = position.to_offset(&display_map, Bias::Left);
1584 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
1585
1586 let mut pending_selection = self
1587 .selections
1588 .pending_anchor()
1589 .expect("extend_selection not called with pending selection");
1590 if position >= tail {
1591 pending_selection.start = tail_anchor;
1592 } else {
1593 pending_selection.end = tail_anchor;
1594 pending_selection.reversed = true;
1595 }
1596
1597 let mut pending_mode = self.selections.pending_mode().unwrap();
1598 match &mut pending_mode {
1599 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
1600 _ => {}
1601 }
1602
1603 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
1604 s.set_pending(pending_selection, pending_mode)
1605 });
1606 }
1607
1608 fn begin_selection(
1609 &mut self,
1610 position: DisplayPoint,
1611 add: bool,
1612 click_count: usize,
1613 cx: &mut ViewContext<Self>,
1614 ) {
1615 if !self.focused {
1616 cx.focus_self();
1617 }
1618
1619 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1620 let buffer = &display_map.buffer_snapshot;
1621 let newest_selection = self.selections.newest_anchor().clone();
1622 let position = display_map.clip_point(position, Bias::Left);
1623
1624 let start;
1625 let end;
1626 let mode;
1627 let auto_scroll;
1628 match click_count {
1629 1 => {
1630 start = buffer.anchor_before(position.to_point(&display_map));
1631 end = start.clone();
1632 mode = SelectMode::Character;
1633 auto_scroll = true;
1634 }
1635 2 => {
1636 let range = movement::surrounding_word(&display_map, position);
1637 start = buffer.anchor_before(range.start.to_point(&display_map));
1638 end = buffer.anchor_before(range.end.to_point(&display_map));
1639 mode = SelectMode::Word(start.clone()..end.clone());
1640 auto_scroll = true;
1641 }
1642 3 => {
1643 let position = display_map
1644 .clip_point(position, Bias::Left)
1645 .to_point(&display_map);
1646 let line_start = display_map.prev_line_boundary(position).0;
1647 let next_line_start = buffer.clip_point(
1648 display_map.next_line_boundary(position).0 + Point::new(1, 0),
1649 Bias::Left,
1650 );
1651 start = buffer.anchor_before(line_start);
1652 end = buffer.anchor_before(next_line_start);
1653 mode = SelectMode::Line(start.clone()..end.clone());
1654 auto_scroll = true;
1655 }
1656 _ => {
1657 start = buffer.anchor_before(0);
1658 end = buffer.anchor_before(buffer.len());
1659 mode = SelectMode::All;
1660 auto_scroll = false;
1661 }
1662 }
1663
1664 self.change_selections(auto_scroll.then(|| Autoscroll::newest()), cx, |s| {
1665 if !add {
1666 s.clear_disjoint();
1667 } else if click_count > 1 {
1668 s.delete(newest_selection.id)
1669 }
1670
1671 s.set_pending_anchor_range(start..end, mode);
1672 });
1673 }
1674
1675 fn begin_columnar_selection(
1676 &mut self,
1677 position: DisplayPoint,
1678 goal_column: u32,
1679 cx: &mut ViewContext<Self>,
1680 ) {
1681 if !self.focused {
1682 cx.focus_self();
1683 }
1684
1685 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1686 let tail = self.selections.newest::<Point>(cx).tail();
1687 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
1688
1689 self.select_columns(
1690 tail.to_display_point(&display_map),
1691 position,
1692 goal_column,
1693 &display_map,
1694 cx,
1695 );
1696 }
1697
1698 fn update_selection(
1699 &mut self,
1700 position: DisplayPoint,
1701 goal_column: u32,
1702 scroll_position: Vector2F,
1703 cx: &mut ViewContext<Self>,
1704 ) {
1705 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1706
1707 if let Some(tail) = self.columnar_selection_tail.as_ref() {
1708 let tail = tail.to_display_point(&display_map);
1709 self.select_columns(tail, position, goal_column, &display_map, cx);
1710 } else if let Some(mut pending) = self.selections.pending_anchor() {
1711 let buffer = self.buffer.read(cx).snapshot(cx);
1712 let head;
1713 let tail;
1714 let mode = self.selections.pending_mode().unwrap();
1715 match &mode {
1716 SelectMode::Character => {
1717 head = position.to_point(&display_map);
1718 tail = pending.tail().to_point(&buffer);
1719 }
1720 SelectMode::Word(original_range) => {
1721 let original_display_range = original_range.start.to_display_point(&display_map)
1722 ..original_range.end.to_display_point(&display_map);
1723 let original_buffer_range = original_display_range.start.to_point(&display_map)
1724 ..original_display_range.end.to_point(&display_map);
1725 if movement::is_inside_word(&display_map, position)
1726 || original_display_range.contains(&position)
1727 {
1728 let word_range = movement::surrounding_word(&display_map, position);
1729 if word_range.start < original_display_range.start {
1730 head = word_range.start.to_point(&display_map);
1731 } else {
1732 head = word_range.end.to_point(&display_map);
1733 }
1734 } else {
1735 head = position.to_point(&display_map);
1736 }
1737
1738 if head <= original_buffer_range.start {
1739 tail = original_buffer_range.end;
1740 } else {
1741 tail = original_buffer_range.start;
1742 }
1743 }
1744 SelectMode::Line(original_range) => {
1745 let original_range = original_range.to_point(&display_map.buffer_snapshot);
1746
1747 let position = display_map
1748 .clip_point(position, Bias::Left)
1749 .to_point(&display_map);
1750 let line_start = display_map.prev_line_boundary(position).0;
1751 let next_line_start = buffer.clip_point(
1752 display_map.next_line_boundary(position).0 + Point::new(1, 0),
1753 Bias::Left,
1754 );
1755
1756 if line_start < original_range.start {
1757 head = line_start
1758 } else {
1759 head = next_line_start
1760 }
1761
1762 if head <= original_range.start {
1763 tail = original_range.end;
1764 } else {
1765 tail = original_range.start;
1766 }
1767 }
1768 SelectMode::All => {
1769 return;
1770 }
1771 };
1772
1773 if head < tail {
1774 pending.start = buffer.anchor_before(head);
1775 pending.end = buffer.anchor_before(tail);
1776 pending.reversed = true;
1777 } else {
1778 pending.start = buffer.anchor_before(tail);
1779 pending.end = buffer.anchor_before(head);
1780 pending.reversed = false;
1781 }
1782
1783 self.change_selections(None, cx, |s| {
1784 s.set_pending(pending, mode);
1785 });
1786 } else {
1787 log::error!("update_selection dispatched with no pending selection");
1788 return;
1789 }
1790
1791 self.set_scroll_position(scroll_position, cx);
1792 cx.notify();
1793 }
1794
1795 fn end_selection(&mut self, cx: &mut ViewContext<Self>) {
1796 self.columnar_selection_tail.take();
1797 if self.selections.pending_anchor().is_some() {
1798 let selections = self.selections.all::<usize>(cx);
1799 self.change_selections(None, cx, |s| {
1800 s.select(selections);
1801 s.clear_pending();
1802 });
1803 }
1804 }
1805
1806 fn select_columns(
1807 &mut self,
1808 tail: DisplayPoint,
1809 head: DisplayPoint,
1810 goal_column: u32,
1811 display_map: &DisplaySnapshot,
1812 cx: &mut ViewContext<Self>,
1813 ) {
1814 let start_row = cmp::min(tail.row(), head.row());
1815 let end_row = cmp::max(tail.row(), head.row());
1816 let start_column = cmp::min(tail.column(), goal_column);
1817 let end_column = cmp::max(tail.column(), goal_column);
1818 let reversed = start_column < tail.column();
1819
1820 let selection_ranges = (start_row..=end_row)
1821 .filter_map(|row| {
1822 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
1823 let start = display_map
1824 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
1825 .to_point(display_map);
1826 let end = display_map
1827 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
1828 .to_point(display_map);
1829 if reversed {
1830 Some(end..start)
1831 } else {
1832 Some(start..end)
1833 }
1834 } else {
1835 None
1836 }
1837 })
1838 .collect::<Vec<_>>();
1839
1840 self.change_selections(None, cx, |s| {
1841 s.select_ranges(selection_ranges);
1842 });
1843 cx.notify();
1844 }
1845
1846 pub fn has_pending_nonempty_selection(&self) -> bool {
1847 let pending_nonempty_selection = match self.selections.pending_anchor() {
1848 Some(Selection { start, end, .. }) => start != end,
1849 None => false,
1850 };
1851 pending_nonempty_selection || self.columnar_selection_tail.is_some()
1852 }
1853
1854 pub fn has_pending_selection(&self) -> bool {
1855 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
1856 }
1857
1858 pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
1859 if self.take_rename(false, cx).is_some() {
1860 return;
1861 }
1862
1863 if hide_hover(self, &HideHover, cx) {
1864 return;
1865 }
1866
1867 if self.hide_context_menu(cx).is_some() {
1868 return;
1869 }
1870
1871 if self.hide_copilot_suggestion(cx).is_some() {
1872 return;
1873 }
1874
1875 if self.snippet_stack.pop().is_some() {
1876 return;
1877 }
1878
1879 if self.mode == EditorMode::Full {
1880 if self.active_diagnostics.is_some() {
1881 self.dismiss_diagnostics(cx);
1882 return;
1883 }
1884
1885 if self.change_selections(Some(Autoscroll::fit()), cx, |s| s.try_cancel()) {
1886 return;
1887 }
1888 }
1889
1890 cx.propagate_action();
1891 }
1892
1893 pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
1894 let text: Arc<str> = text.into();
1895
1896 if !self.input_enabled {
1897 cx.emit(Event::InputIgnored { text });
1898 return;
1899 }
1900
1901 let selections = self.selections.all_adjusted(cx);
1902 let mut edits = Vec::new();
1903 let mut new_selections = Vec::with_capacity(selections.len());
1904 let mut new_autoclose_regions = Vec::new();
1905 let snapshot = self.buffer.read(cx).read(cx);
1906
1907 for (selection, autoclose_region) in
1908 self.selections_with_autoclose_regions(selections, &snapshot)
1909 {
1910 if let Some(language) = snapshot.language_scope_at(selection.head()) {
1911 // Determine if the inserted text matches the opening or closing
1912 // bracket of any of this language's bracket pairs.
1913 let mut bracket_pair = None;
1914 let mut is_bracket_pair_start = false;
1915 for (pair, enabled) in language.brackets() {
1916 if enabled && pair.close && pair.start.ends_with(text.as_ref()) {
1917 bracket_pair = Some(pair.clone());
1918 is_bracket_pair_start = true;
1919 break;
1920 } else if pair.end.as_str() == text.as_ref() {
1921 bracket_pair = Some(pair.clone());
1922 break;
1923 }
1924 }
1925
1926 if let Some(bracket_pair) = bracket_pair {
1927 if selection.is_empty() {
1928 if is_bracket_pair_start {
1929 let prefix_len = bracket_pair.start.len() - text.len();
1930
1931 // If the inserted text is a suffix of an opening bracket and the
1932 // selection is preceded by the rest of the opening bracket, then
1933 // insert the closing bracket.
1934 let following_text_allows_autoclose = snapshot
1935 .chars_at(selection.start)
1936 .next()
1937 .map_or(true, |c| language.should_autoclose_before(c));
1938 let preceding_text_matches_prefix = prefix_len == 0
1939 || (selection.start.column >= (prefix_len as u32)
1940 && snapshot.contains_str_at(
1941 Point::new(
1942 selection.start.row,
1943 selection.start.column - (prefix_len as u32),
1944 ),
1945 &bracket_pair.start[..prefix_len],
1946 ));
1947 if following_text_allows_autoclose && preceding_text_matches_prefix {
1948 let anchor = snapshot.anchor_before(selection.end);
1949 new_selections.push((selection.map(|_| anchor), text.len()));
1950 new_autoclose_regions.push((
1951 anchor,
1952 text.len(),
1953 selection.id,
1954 bracket_pair.clone(),
1955 ));
1956 edits.push((
1957 selection.range(),
1958 format!("{}{}", text, bracket_pair.end).into(),
1959 ));
1960 continue;
1961 }
1962 }
1963
1964 if let Some(region) = autoclose_region {
1965 // If the selection is followed by an auto-inserted closing bracket,
1966 // then don't insert that closing bracket again; just move the selection
1967 // past the closing bracket.
1968 let should_skip = selection.end == region.range.end.to_point(&snapshot)
1969 && text.as_ref() == region.pair.end.as_str();
1970 if should_skip {
1971 let anchor = snapshot.anchor_after(selection.end);
1972 new_selections
1973 .push((selection.map(|_| anchor), region.pair.end.len()));
1974 continue;
1975 }
1976 }
1977 }
1978 // If an opening bracket is 1 character long and is typed while
1979 // text is selected, then surround that text with the bracket pair.
1980 else if is_bracket_pair_start && bracket_pair.start.chars().count() == 1 {
1981 edits.push((selection.start..selection.start, text.clone()));
1982 edits.push((
1983 selection.end..selection.end,
1984 bracket_pair.end.as_str().into(),
1985 ));
1986 new_selections.push((
1987 Selection {
1988 id: selection.id,
1989 start: snapshot.anchor_after(selection.start),
1990 end: snapshot.anchor_before(selection.end),
1991 reversed: selection.reversed,
1992 goal: selection.goal,
1993 },
1994 0,
1995 ));
1996 continue;
1997 }
1998 }
1999 }
2000
2001 // If not handling any auto-close operation, then just replace the selected
2002 // text with the given input and move the selection to the end of the
2003 // newly inserted text.
2004 let anchor = snapshot.anchor_after(selection.end);
2005 new_selections.push((selection.map(|_| anchor), 0));
2006 edits.push((selection.start..selection.end, text.clone()));
2007 }
2008
2009 drop(snapshot);
2010 self.transact(cx, |this, cx| {
2011 this.buffer.update(cx, |buffer, cx| {
2012 buffer.edit(edits, Some(AutoindentMode::EachLine), cx);
2013 });
2014
2015 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
2016 let new_selection_deltas = new_selections.iter().map(|e| e.1);
2017 let snapshot = this.buffer.read(cx).read(cx);
2018 let new_selections = resolve_multiple::<usize, _>(new_anchor_selections, &snapshot)
2019 .zip(new_selection_deltas)
2020 .map(|(selection, delta)| selection.map(|e| e + delta))
2021 .collect::<Vec<_>>();
2022
2023 let mut i = 0;
2024 for (position, delta, selection_id, pair) in new_autoclose_regions {
2025 let position = position.to_offset(&snapshot) + delta;
2026 let start = snapshot.anchor_before(position);
2027 let end = snapshot.anchor_after(position);
2028 while let Some(existing_state) = this.autoclose_regions.get(i) {
2029 match existing_state.range.start.cmp(&start, &snapshot) {
2030 Ordering::Less => i += 1,
2031 Ordering::Greater => break,
2032 Ordering::Equal => match end.cmp(&existing_state.range.end, &snapshot) {
2033 Ordering::Less => i += 1,
2034 Ordering::Equal => break,
2035 Ordering::Greater => break,
2036 },
2037 }
2038 }
2039 this.autoclose_regions.insert(
2040 i,
2041 AutocloseRegion {
2042 selection_id,
2043 range: start..end,
2044 pair,
2045 },
2046 );
2047 }
2048
2049 drop(snapshot);
2050 let had_active_copilot_suggestion = this.has_active_copilot_suggestion(cx);
2051 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
2052
2053 if had_active_copilot_suggestion {
2054 this.refresh_copilot_suggestions(cx);
2055 if !this.has_active_copilot_suggestion(cx) {
2056 this.trigger_completion_on_input(&text, cx);
2057 }
2058 } else {
2059 this.trigger_completion_on_input(&text, cx);
2060 this.refresh_copilot_suggestions(cx);
2061 }
2062 });
2063 }
2064
2065 pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
2066 self.transact(cx, |this, cx| {
2067 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
2068 let selections = this.selections.all::<usize>(cx);
2069
2070 let buffer = this.buffer.read(cx).snapshot(cx);
2071 selections
2072 .iter()
2073 .map(|selection| {
2074 let start_point = selection.start.to_point(&buffer);
2075 let mut indent = buffer.indent_size_for_line(start_point.row);
2076 indent.len = cmp::min(indent.len, start_point.column);
2077 let start = selection.start;
2078 let end = selection.end;
2079
2080 let mut insert_extra_newline = false;
2081 if let Some(language) = buffer.language_scope_at(start) {
2082 let leading_whitespace_len = buffer
2083 .reversed_chars_at(start)
2084 .take_while(|c| c.is_whitespace() && *c != '\n')
2085 .map(|c| c.len_utf8())
2086 .sum::<usize>();
2087
2088 let trailing_whitespace_len = buffer
2089 .chars_at(end)
2090 .take_while(|c| c.is_whitespace() && *c != '\n')
2091 .map(|c| c.len_utf8())
2092 .sum::<usize>();
2093
2094 insert_extra_newline = language.brackets().any(|(pair, enabled)| {
2095 let pair_start = pair.start.trim_end();
2096 let pair_end = pair.end.trim_start();
2097
2098 enabled
2099 && pair.newline
2100 && buffer
2101 .contains_str_at(end + trailing_whitespace_len, pair_end)
2102 && buffer.contains_str_at(
2103 (start - leading_whitespace_len)
2104 .saturating_sub(pair_start.len()),
2105 pair_start,
2106 )
2107 });
2108 }
2109
2110 let mut new_text = String::with_capacity(1 + indent.len as usize);
2111 new_text.push('\n');
2112 new_text.extend(indent.chars());
2113 if insert_extra_newline {
2114 new_text = new_text.repeat(2);
2115 }
2116
2117 let anchor = buffer.anchor_after(end);
2118 let new_selection = selection.map(|_| anchor);
2119 (
2120 (start..end, new_text),
2121 (insert_extra_newline, new_selection),
2122 )
2123 })
2124 .unzip()
2125 };
2126
2127 this.edit_with_autoindent(edits, cx);
2128 let buffer = this.buffer.read(cx).snapshot(cx);
2129 let new_selections = selection_fixup_info
2130 .into_iter()
2131 .map(|(extra_newline_inserted, new_selection)| {
2132 let mut cursor = new_selection.end.to_point(&buffer);
2133 if extra_newline_inserted {
2134 cursor.row -= 1;
2135 cursor.column = buffer.line_len(cursor.row);
2136 }
2137 new_selection.map(|_| cursor)
2138 })
2139 .collect();
2140
2141 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
2142 this.refresh_copilot_suggestions(cx);
2143 });
2144 }
2145
2146 pub fn newline_below(&mut self, _: &NewlineBelow, cx: &mut ViewContext<Self>) {
2147 let buffer = self.buffer.read(cx);
2148 let snapshot = buffer.snapshot(cx);
2149
2150 let mut edits = Vec::new();
2151 let mut rows = Vec::new();
2152 let mut rows_inserted = 0;
2153
2154 for selection in self.selections.all_adjusted(cx) {
2155 let cursor = selection.head();
2156 let row = cursor.row;
2157
2158 let end_of_line = snapshot
2159 .clip_point(Point::new(row, snapshot.line_len(row)), Bias::Left)
2160 .to_point(&snapshot);
2161
2162 let newline = "\n".to_string();
2163 edits.push((end_of_line..end_of_line, newline));
2164
2165 rows_inserted += 1;
2166 rows.push(row + rows_inserted);
2167 }
2168
2169 self.transact(cx, |editor, cx| {
2170 editor.edit_with_autoindent(edits, cx);
2171
2172 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
2173 let mut index = 0;
2174 s.move_cursors_with(|map, _, _| {
2175 let row = rows[index];
2176 index += 1;
2177
2178 let point = Point::new(row, 0);
2179 let boundary = map.next_line_boundary(point).1;
2180 let clipped = map.clip_point(boundary, Bias::Left);
2181
2182 (clipped, SelectionGoal::None)
2183 });
2184 });
2185 });
2186 }
2187
2188 pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2189 self.insert_with_autoindent_mode(
2190 text,
2191 Some(AutoindentMode::Block {
2192 original_indent_columns: Vec::new(),
2193 }),
2194 cx,
2195 );
2196 }
2197
2198 fn insert_with_autoindent_mode(
2199 &mut self,
2200 text: &str,
2201 autoindent_mode: Option<AutoindentMode>,
2202 cx: &mut ViewContext<Self>,
2203 ) {
2204 let text: Arc<str> = text.into();
2205 self.transact(cx, |this, cx| {
2206 let old_selections = this.selections.all_adjusted(cx);
2207 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
2208 let anchors = {
2209 let snapshot = buffer.read(cx);
2210 old_selections
2211 .iter()
2212 .map(|s| {
2213 let anchor = snapshot.anchor_after(s.end);
2214 s.map(|_| anchor)
2215 })
2216 .collect::<Vec<_>>()
2217 };
2218 buffer.edit(
2219 old_selections
2220 .iter()
2221 .map(|s| (s.start..s.end, text.clone())),
2222 autoindent_mode,
2223 cx,
2224 );
2225 anchors
2226 });
2227
2228 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
2229 s.select_anchors(selection_anchors);
2230 })
2231 });
2232 }
2233
2234 fn trigger_completion_on_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2235 if !cx.global::<Settings>().show_completions_on_input {
2236 return;
2237 }
2238
2239 let selection = self.selections.newest_anchor();
2240 if self
2241 .buffer
2242 .read(cx)
2243 .is_completion_trigger(selection.head(), text, cx)
2244 {
2245 self.show_completions(&ShowCompletions, cx);
2246 } else {
2247 self.hide_context_menu(cx);
2248 }
2249 }
2250
2251 /// If any empty selections is touching the start of its innermost containing autoclose
2252 /// region, expand it to select the brackets.
2253 fn select_autoclose_pair(&mut self, cx: &mut ViewContext<Self>) {
2254 let selections = self.selections.all::<usize>(cx);
2255 let buffer = self.buffer.read(cx).read(cx);
2256 let mut new_selections = Vec::new();
2257 for (mut selection, region) in self.selections_with_autoclose_regions(selections, &buffer) {
2258 if let (Some(region), true) = (region, selection.is_empty()) {
2259 let mut range = region.range.to_offset(&buffer);
2260 if selection.start == range.start {
2261 if range.start >= region.pair.start.len() {
2262 range.start -= region.pair.start.len();
2263 if buffer.contains_str_at(range.start, ®ion.pair.start) {
2264 if buffer.contains_str_at(range.end, ®ion.pair.end) {
2265 range.end += region.pair.end.len();
2266 selection.start = range.start;
2267 selection.end = range.end;
2268 }
2269 }
2270 }
2271 }
2272 }
2273 new_selections.push(selection);
2274 }
2275
2276 drop(buffer);
2277 self.change_selections(None, cx, |selections| selections.select(new_selections));
2278 }
2279
2280 /// Iterate the given selections, and for each one, find the smallest surrounding
2281 /// autoclose region. This uses the ordering of the selections and the autoclose
2282 /// regions to avoid repeated comparisons.
2283 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
2284 &'a self,
2285 selections: impl IntoIterator<Item = Selection<D>>,
2286 buffer: &'a MultiBufferSnapshot,
2287 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
2288 let mut i = 0;
2289 let mut regions = self.autoclose_regions.as_slice();
2290 selections.into_iter().map(move |selection| {
2291 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
2292
2293 let mut enclosing = None;
2294 while let Some(pair_state) = regions.get(i) {
2295 if pair_state.range.end.to_offset(buffer) < range.start {
2296 regions = ®ions[i + 1..];
2297 i = 0;
2298 } else if pair_state.range.start.to_offset(buffer) > range.end {
2299 break;
2300 } else if pair_state.selection_id == selection.id {
2301 enclosing = Some(pair_state);
2302 i += 1;
2303 }
2304 }
2305
2306 (selection.clone(), enclosing)
2307 })
2308 }
2309
2310 /// Remove any autoclose regions that no longer contain their selection.
2311 fn invalidate_autoclose_regions(
2312 &mut self,
2313 mut selections: &[Selection<Anchor>],
2314 buffer: &MultiBufferSnapshot,
2315 ) {
2316 self.autoclose_regions.retain(|state| {
2317 let mut i = 0;
2318 while let Some(selection) = selections.get(i) {
2319 if selection.end.cmp(&state.range.start, buffer).is_lt() {
2320 selections = &selections[1..];
2321 continue;
2322 }
2323 if selection.start.cmp(&state.range.end, buffer).is_gt() {
2324 break;
2325 }
2326 if selection.id == state.selection_id {
2327 return true;
2328 } else {
2329 i += 1;
2330 }
2331 }
2332 false
2333 });
2334 }
2335
2336 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
2337 let offset = position.to_offset(buffer);
2338 let (word_range, kind) = buffer.surrounding_word(offset);
2339 if offset > word_range.start && kind == Some(CharKind::Word) {
2340 Some(
2341 buffer
2342 .text_for_range(word_range.start..offset)
2343 .collect::<String>(),
2344 )
2345 } else {
2346 None
2347 }
2348 }
2349
2350 fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) {
2351 if self.pending_rename.is_some() {
2352 return;
2353 }
2354
2355 let project = if let Some(project) = self.project.clone() {
2356 project
2357 } else {
2358 return;
2359 };
2360
2361 let position = self.selections.newest_anchor().head();
2362 let (buffer, buffer_position) = if let Some(output) = self
2363 .buffer
2364 .read(cx)
2365 .text_anchor_for_position(position.clone(), cx)
2366 {
2367 output
2368 } else {
2369 return;
2370 };
2371
2372 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position.clone());
2373 let completions = project.update(cx, |project, cx| {
2374 project.completions(&buffer, buffer_position, cx)
2375 });
2376
2377 let id = post_inc(&mut self.next_completion_id);
2378 let task = cx.spawn_weak(|this, mut cx| {
2379 async move {
2380 let menu = if let Some(completions) = completions.await.log_err() {
2381 let mut menu = CompletionsMenu {
2382 id,
2383 initial_position: position,
2384 match_candidates: completions
2385 .iter()
2386 .enumerate()
2387 .map(|(id, completion)| {
2388 StringMatchCandidate::new(
2389 id,
2390 completion.label.text[completion.label.filter_range.clone()]
2391 .into(),
2392 )
2393 })
2394 .collect(),
2395 buffer,
2396 completions: completions.into(),
2397 matches: Vec::new().into(),
2398 selected_item: 0,
2399 list: Default::default(),
2400 };
2401 menu.filter(query.as_deref(), cx.background()).await;
2402 if menu.matches.is_empty() {
2403 None
2404 } else {
2405 Some(menu)
2406 }
2407 } else {
2408 None
2409 };
2410
2411 let this = this
2412 .upgrade(&cx)
2413 .ok_or_else(|| anyhow!("editor was dropped"))?;
2414 this.update(&mut cx, |this, cx| {
2415 this.completion_tasks.retain(|(task_id, _)| *task_id > id);
2416
2417 match this.context_menu.as_ref() {
2418 None => {}
2419 Some(ContextMenu::Completions(prev_menu)) => {
2420 if prev_menu.id > id {
2421 return;
2422 }
2423 }
2424 _ => return,
2425 }
2426
2427 if this.focused && menu.is_some() {
2428 let menu = menu.unwrap();
2429 this.show_context_menu(ContextMenu::Completions(menu), cx);
2430 } else if this.completion_tasks.is_empty() {
2431 // If there are no more completion tasks and the last menu was
2432 // empty, we should hide it. If it was already hidden, we should
2433 // also show the copilot suggestion when available.
2434 if this.hide_context_menu(cx).is_none() {
2435 this.update_visible_copilot_suggestion(cx);
2436 }
2437 }
2438 });
2439
2440 Ok::<_, anyhow::Error>(())
2441 }
2442 .log_err()
2443 });
2444 self.completion_tasks.push((id, task));
2445 }
2446
2447 pub fn confirm_completion(
2448 &mut self,
2449 action: &ConfirmCompletion,
2450 cx: &mut ViewContext<Self>,
2451 ) -> Option<Task<Result<()>>> {
2452 use language::ToOffset as _;
2453
2454 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
2455 menu
2456 } else {
2457 return None;
2458 };
2459
2460 let mat = completions_menu
2461 .matches
2462 .get(action.item_ix.unwrap_or(completions_menu.selected_item))?;
2463 let buffer_handle = completions_menu.buffer;
2464 let completion = completions_menu.completions.get(mat.candidate_id)?;
2465
2466 let snippet;
2467 let text;
2468 if completion.is_snippet() {
2469 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
2470 text = snippet.as_ref().unwrap().text.clone();
2471 } else {
2472 snippet = None;
2473 text = completion.new_text.clone();
2474 };
2475 let selections = self.selections.all::<usize>(cx);
2476 let buffer = buffer_handle.read(cx);
2477 let old_range = completion.old_range.to_offset(buffer);
2478 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
2479
2480 let newest_selection = self.selections.newest_anchor();
2481 if newest_selection.start.buffer_id != Some(buffer_handle.id()) {
2482 return None;
2483 }
2484
2485 let lookbehind = newest_selection
2486 .start
2487 .text_anchor
2488 .to_offset(buffer)
2489 .saturating_sub(old_range.start);
2490 let lookahead = old_range
2491 .end
2492 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
2493 let mut common_prefix_len = old_text
2494 .bytes()
2495 .zip(text.bytes())
2496 .take_while(|(a, b)| a == b)
2497 .count();
2498
2499 let snapshot = self.buffer.read(cx).snapshot(cx);
2500 let mut ranges = Vec::new();
2501 for selection in &selections {
2502 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
2503 let start = selection.start.saturating_sub(lookbehind);
2504 let end = selection.end + lookahead;
2505 ranges.push(start + common_prefix_len..end);
2506 } else {
2507 common_prefix_len = 0;
2508 ranges.clear();
2509 ranges.extend(selections.iter().map(|s| {
2510 if s.id == newest_selection.id {
2511 old_range.clone()
2512 } else {
2513 s.start..s.end
2514 }
2515 }));
2516 break;
2517 }
2518 }
2519 let text = &text[common_prefix_len..];
2520
2521 self.transact(cx, |this, cx| {
2522 if let Some(mut snippet) = snippet {
2523 snippet.text = text.to_string();
2524 for tabstop in snippet.tabstops.iter_mut().flatten() {
2525 tabstop.start -= common_prefix_len as isize;
2526 tabstop.end -= common_prefix_len as isize;
2527 }
2528
2529 this.insert_snippet(&ranges, snippet, cx).log_err();
2530 } else {
2531 this.buffer.update(cx, |buffer, cx| {
2532 buffer.edit(
2533 ranges.iter().map(|range| (range.clone(), text)),
2534 Some(AutoindentMode::EachLine),
2535 cx,
2536 );
2537 });
2538 }
2539
2540 this.refresh_copilot_suggestions(cx);
2541 });
2542
2543 let project = self.project.clone()?;
2544 let apply_edits = project.update(cx, |project, cx| {
2545 project.apply_additional_edits_for_completion(
2546 buffer_handle,
2547 completion.clone(),
2548 true,
2549 cx,
2550 )
2551 });
2552 Some(cx.foreground().spawn(async move {
2553 apply_edits.await?;
2554 Ok(())
2555 }))
2556 }
2557
2558 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
2559 if matches!(
2560 self.context_menu.as_ref(),
2561 Some(ContextMenu::CodeActions(_))
2562 ) {
2563 self.context_menu.take();
2564 cx.notify();
2565 return;
2566 }
2567
2568 let deployed_from_indicator = action.deployed_from_indicator;
2569 let mut task = self.code_actions_task.take();
2570 cx.spawn_weak(|this, mut cx| async move {
2571 while let Some(prev_task) = task {
2572 prev_task.await;
2573 task = this
2574 .upgrade(&cx)
2575 .and_then(|this| this.update(&mut cx, |this, _| this.code_actions_task.take()));
2576 }
2577
2578 if let Some(this) = this.upgrade(&cx) {
2579 this.update(&mut cx, |this, cx| {
2580 if this.focused {
2581 if let Some((buffer, actions)) = this.available_code_actions.clone() {
2582 this.show_context_menu(
2583 ContextMenu::CodeActions(CodeActionsMenu {
2584 buffer,
2585 actions,
2586 selected_item: Default::default(),
2587 list: Default::default(),
2588 deployed_from_indicator,
2589 }),
2590 cx,
2591 );
2592 }
2593 }
2594 })
2595 }
2596 Ok::<_, anyhow::Error>(())
2597 })
2598 .detach_and_log_err(cx);
2599 }
2600
2601 pub fn confirm_code_action(
2602 workspace: &mut Workspace,
2603 action: &ConfirmCodeAction,
2604 cx: &mut ViewContext<Workspace>,
2605 ) -> Option<Task<Result<()>>> {
2606 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
2607 let actions_menu = if let ContextMenu::CodeActions(menu) =
2608 editor.update(cx, |editor, cx| editor.hide_context_menu(cx))?
2609 {
2610 menu
2611 } else {
2612 return None;
2613 };
2614 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
2615 let action = actions_menu.actions.get(action_ix)?.clone();
2616 let title = action.lsp_action.title.clone();
2617 let buffer = actions_menu.buffer;
2618
2619 let apply_code_actions = workspace.project().clone().update(cx, |project, cx| {
2620 project.apply_code_action(buffer, action, true, cx)
2621 });
2622 Some(cx.spawn(|workspace, cx| async move {
2623 let project_transaction = apply_code_actions.await?;
2624 Self::open_project_transaction(editor, workspace, project_transaction, title, cx).await
2625 }))
2626 }
2627
2628 async fn open_project_transaction(
2629 this: ViewHandle<Editor>,
2630 workspace: ViewHandle<Workspace>,
2631 transaction: ProjectTransaction,
2632 title: String,
2633 mut cx: AsyncAppContext,
2634 ) -> Result<()> {
2635 let replica_id = this.read_with(&cx, |this, cx| this.replica_id(cx));
2636
2637 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
2638 entries.sort_unstable_by_key(|(buffer, _)| {
2639 buffer.read_with(&cx, |buffer, _| buffer.file().map(|f| f.path().clone()))
2640 });
2641
2642 // If the project transaction's edits are all contained within this editor, then
2643 // avoid opening a new editor to display them.
2644
2645 if let Some((buffer, transaction)) = entries.first() {
2646 if entries.len() == 1 {
2647 let excerpt = this.read_with(&cx, |editor, cx| {
2648 editor
2649 .buffer()
2650 .read(cx)
2651 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
2652 });
2653 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
2654 if excerpted_buffer == *buffer {
2655 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
2656 let excerpt_range = excerpt_range.to_offset(buffer);
2657 buffer
2658 .edited_ranges_for_transaction::<usize>(transaction)
2659 .all(|range| {
2660 excerpt_range.start <= range.start
2661 && excerpt_range.end >= range.end
2662 })
2663 });
2664
2665 if all_edits_within_excerpt {
2666 return Ok(());
2667 }
2668 }
2669 }
2670 }
2671 } else {
2672 return Ok(());
2673 }
2674
2675 let mut ranges_to_highlight = Vec::new();
2676 let excerpt_buffer = cx.add_model(|cx| {
2677 let mut multibuffer = MultiBuffer::new(replica_id).with_title(title);
2678 for (buffer_handle, transaction) in &entries {
2679 let buffer = buffer_handle.read(cx);
2680 ranges_to_highlight.extend(
2681 multibuffer.push_excerpts_with_context_lines(
2682 buffer_handle.clone(),
2683 buffer
2684 .edited_ranges_for_transaction::<usize>(transaction)
2685 .collect(),
2686 1,
2687 cx,
2688 ),
2689 );
2690 }
2691 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)));
2692 multibuffer
2693 });
2694
2695 workspace.update(&mut cx, |workspace, cx| {
2696 let project = workspace.project().clone();
2697 let editor =
2698 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
2699 workspace.add_item(Box::new(editor.clone()), cx);
2700 editor.update(cx, |editor, cx| {
2701 editor.highlight_background::<Self>(
2702 ranges_to_highlight,
2703 |theme| theme.editor.highlighted_line_background,
2704 cx,
2705 );
2706 });
2707 });
2708
2709 Ok(())
2710 }
2711
2712 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
2713 let project = self.project.as_ref()?;
2714 let buffer = self.buffer.read(cx);
2715 let newest_selection = self.selections.newest_anchor().clone();
2716 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
2717 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
2718 if start_buffer != end_buffer {
2719 return None;
2720 }
2721
2722 let actions = project.update(cx, |project, cx| {
2723 project.code_actions(&start_buffer, start..end, cx)
2724 });
2725 self.code_actions_task = Some(cx.spawn_weak(|this, mut cx| async move {
2726 let actions = actions.await;
2727 if let Some(this) = this.upgrade(&cx) {
2728 this.update(&mut cx, |this, cx| {
2729 this.available_code_actions = actions.log_err().and_then(|actions| {
2730 if actions.is_empty() {
2731 None
2732 } else {
2733 Some((start_buffer, actions.into()))
2734 }
2735 });
2736 cx.notify();
2737 })
2738 }
2739 }));
2740 None
2741 }
2742
2743 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
2744 if self.pending_rename.is_some() {
2745 return None;
2746 }
2747
2748 let project = self.project.as_ref()?;
2749 let buffer = self.buffer.read(cx);
2750 let newest_selection = self.selections.newest_anchor().clone();
2751 let cursor_position = newest_selection.head();
2752 let (cursor_buffer, cursor_buffer_position) =
2753 buffer.text_anchor_for_position(cursor_position.clone(), cx)?;
2754 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
2755 if cursor_buffer != tail_buffer {
2756 return None;
2757 }
2758
2759 let highlights = project.update(cx, |project, cx| {
2760 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
2761 });
2762
2763 self.document_highlights_task = Some(cx.spawn_weak(|this, mut cx| async move {
2764 let highlights = highlights.log_err().await;
2765 if let Some((this, highlights)) = this.upgrade(&cx).zip(highlights) {
2766 this.update(&mut cx, |this, cx| {
2767 if this.pending_rename.is_some() {
2768 return;
2769 }
2770
2771 let buffer_id = cursor_position.buffer_id;
2772 let buffer = this.buffer.read(cx);
2773 if !buffer
2774 .text_anchor_for_position(cursor_position, cx)
2775 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
2776 {
2777 return;
2778 }
2779
2780 let cursor_buffer_snapshot = cursor_buffer.read(cx);
2781 let mut write_ranges = Vec::new();
2782 let mut read_ranges = Vec::new();
2783 for highlight in highlights {
2784 for (excerpt_id, excerpt_range) in
2785 buffer.excerpts_for_buffer(&cursor_buffer, cx)
2786 {
2787 let start = highlight
2788 .range
2789 .start
2790 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
2791 let end = highlight
2792 .range
2793 .end
2794 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
2795 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
2796 continue;
2797 }
2798
2799 let range = Anchor {
2800 buffer_id,
2801 excerpt_id: excerpt_id.clone(),
2802 text_anchor: start,
2803 }..Anchor {
2804 buffer_id,
2805 excerpt_id,
2806 text_anchor: end,
2807 };
2808 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
2809 write_ranges.push(range);
2810 } else {
2811 read_ranges.push(range);
2812 }
2813 }
2814 }
2815
2816 this.highlight_background::<DocumentHighlightRead>(
2817 read_ranges,
2818 |theme| theme.editor.document_highlight_read_background,
2819 cx,
2820 );
2821 this.highlight_background::<DocumentHighlightWrite>(
2822 write_ranges,
2823 |theme| theme.editor.document_highlight_write_background,
2824 cx,
2825 );
2826 cx.notify();
2827 });
2828 }
2829 }));
2830 None
2831 }
2832
2833 fn refresh_copilot_suggestions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
2834 let copilot = Copilot::global(cx)?;
2835 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
2836 self.clear_copilot_suggestions(cx);
2837 return None;
2838 }
2839 self.update_visible_copilot_suggestion(cx);
2840
2841 let snapshot = self.buffer.read(cx).snapshot(cx);
2842 let cursor = self.selections.newest_anchor().head();
2843 let language_name = snapshot.language_at(cursor).map(|language| language.name());
2844 if !cx
2845 .global::<Settings>()
2846 .show_copilot_suggestions(language_name.as_deref())
2847 {
2848 self.clear_copilot_suggestions(cx);
2849 return None;
2850 }
2851
2852 let (buffer, buffer_position) =
2853 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
2854 self.copilot_state.pending_refresh = cx.spawn_weak(|this, mut cx| async move {
2855 cx.background().timer(COPILOT_DEBOUNCE_TIMEOUT).await;
2856
2857 let completions = copilot
2858 .update(&mut cx, |copilot, cx| {
2859 copilot.completions(&buffer, buffer_position, cx)
2860 })
2861 .await
2862 .log_err()
2863 .into_iter()
2864 .flatten()
2865 .collect_vec();
2866
2867 this.upgrade(&cx)?.update(&mut cx, |this, cx| {
2868 if !completions.is_empty() {
2869 this.copilot_state.cycled = false;
2870 this.copilot_state.pending_cycling_refresh = Task::ready(None);
2871 this.copilot_state.completions.clear();
2872 this.copilot_state.active_completion_index = 0;
2873 this.copilot_state.excerpt_id = Some(cursor.excerpt_id);
2874 for completion in completions {
2875 this.copilot_state.push_completion(completion);
2876 }
2877 this.update_visible_copilot_suggestion(cx);
2878 }
2879 });
2880
2881 Some(())
2882 });
2883
2884 Some(())
2885 }
2886
2887 fn cycle_suggestions(
2888 &mut self,
2889 direction: Direction,
2890 cx: &mut ViewContext<Self>,
2891 ) -> Option<()> {
2892 let copilot = Copilot::global(cx)?;
2893 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
2894 return None;
2895 }
2896
2897 if self.copilot_state.cycled {
2898 self.copilot_state.cycle_completions(direction);
2899 self.update_visible_copilot_suggestion(cx);
2900 } else {
2901 let cursor = self.selections.newest_anchor().head();
2902 let (buffer, buffer_position) =
2903 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
2904 self.copilot_state.pending_cycling_refresh = cx.spawn_weak(|this, mut cx| async move {
2905 let completions = copilot
2906 .update(&mut cx, |copilot, cx| {
2907 copilot.completions_cycling(&buffer, buffer_position, cx)
2908 })
2909 .await;
2910
2911 this.upgrade(&cx)?.update(&mut cx, |this, cx| {
2912 this.copilot_state.cycled = true;
2913 for completion in completions.log_err().into_iter().flatten() {
2914 this.copilot_state.push_completion(completion);
2915 }
2916 this.copilot_state.cycle_completions(direction);
2917 this.update_visible_copilot_suggestion(cx);
2918 });
2919
2920 Some(())
2921 });
2922 }
2923
2924 Some(())
2925 }
2926
2927 fn copilot_suggest(&mut self, _: &copilot::Suggest, cx: &mut ViewContext<Self>) {
2928 if !self.has_active_copilot_suggestion(cx) {
2929 self.refresh_copilot_suggestions(cx);
2930 return;
2931 }
2932
2933 self.update_visible_copilot_suggestion(cx);
2934 }
2935
2936 fn next_copilot_suggestion(&mut self, _: &copilot::NextSuggestion, cx: &mut ViewContext<Self>) {
2937 // if self.has_active_copilot_suggestion(cx) {
2938 self.cycle_suggestions(Direction::Next, cx);
2939 // } else {
2940 // self.refresh_copilot_suggestions(cx);
2941 // }
2942 }
2943
2944 fn previous_copilot_suggestion(
2945 &mut self,
2946 _: &copilot::PreviousSuggestion,
2947 cx: &mut ViewContext<Self>,
2948 ) {
2949 // if self.has_active_copilot_suggestion(cx) {
2950 self.cycle_suggestions(Direction::Prev, cx);
2951 // } else {
2952 // self.refresh_copilot_suggestions(cx);
2953 // }
2954 }
2955
2956 fn accept_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
2957 if let Some(text) = self.hide_copilot_suggestion(cx) {
2958 self.insert_with_autoindent_mode(&text.to_string(), None, cx);
2959 true
2960 } else {
2961 false
2962 }
2963 }
2964
2965 fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool {
2966 self.display_map.read(cx).has_suggestion()
2967 }
2968
2969 fn hide_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> Option<Rope> {
2970 if self.has_active_copilot_suggestion(cx) {
2971 let old_suggestion = self
2972 .display_map
2973 .update(cx, |map, cx| map.replace_suggestion::<usize>(None, cx));
2974 cx.notify();
2975 old_suggestion.map(|suggestion| suggestion.text)
2976 } else {
2977 None
2978 }
2979 }
2980
2981 fn update_visible_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) {
2982 let snapshot = self.buffer.read(cx).snapshot(cx);
2983 let selection = self.selections.newest_anchor();
2984 let cursor = selection.head();
2985
2986 if self.context_menu.is_some()
2987 || !self.completion_tasks.is_empty()
2988 || selection.start != selection.end
2989 {
2990 self.hide_copilot_suggestion(cx);
2991 } else if let Some(text) = self
2992 .copilot_state
2993 .text_for_active_completion(cursor, &snapshot)
2994 {
2995 self.display_map.update(cx, move |map, cx| {
2996 map.replace_suggestion(
2997 Some(Suggestion {
2998 position: cursor,
2999 text: text.trim_end().into(),
3000 }),
3001 cx,
3002 )
3003 });
3004 cx.notify();
3005 } else {
3006 self.hide_copilot_suggestion(cx);
3007 }
3008 }
3009
3010 fn clear_copilot_suggestions(&mut self, cx: &mut ViewContext<Self>) {
3011 self.copilot_state = Default::default();
3012 self.hide_copilot_suggestion(cx);
3013 }
3014
3015 pub fn render_code_actions_indicator(
3016 &self,
3017 style: &EditorStyle,
3018 active: bool,
3019 cx: &mut RenderContext<Self>,
3020 ) -> Option<ElementBox> {
3021 if self.available_code_actions.is_some() {
3022 enum CodeActions {}
3023 Some(
3024 MouseEventHandler::<CodeActions>::new(0, cx, |state, _| {
3025 Svg::new("icons/bolt_8.svg")
3026 .with_color(style.code_actions.indicator.style_for(state, active).color)
3027 .boxed()
3028 })
3029 .with_cursor_style(CursorStyle::PointingHand)
3030 .with_padding(Padding::uniform(3.))
3031 .on_down(MouseButton::Left, |_, cx| {
3032 cx.dispatch_action(ToggleCodeActions {
3033 deployed_from_indicator: true,
3034 });
3035 })
3036 .boxed(),
3037 )
3038 } else {
3039 None
3040 }
3041 }
3042
3043 pub fn render_fold_indicators(
3044 &self,
3045 fold_data: Vec<Option<(FoldStatus, u32, bool)>>,
3046 style: &EditorStyle,
3047 gutter_hovered: bool,
3048 line_height: f32,
3049 gutter_margin: f32,
3050 cx: &mut RenderContext<Self>,
3051 ) -> Vec<Option<ElementBox>> {
3052 enum FoldIndicators {}
3053
3054 let style = style.folds.clone();
3055
3056 fold_data
3057 .iter()
3058 .enumerate()
3059 .map(|(ix, fold_data)| {
3060 fold_data
3061 .map(|(fold_status, buffer_row, active)| {
3062 (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| {
3063 MouseEventHandler::<FoldIndicators>::new(
3064 ix as usize,
3065 cx,
3066 |mouse_state, _| -> ElementBox {
3067 Svg::new(match fold_status {
3068 FoldStatus::Folded => style.folded_icon.clone(),
3069 FoldStatus::Foldable => style.foldable_icon.clone(),
3070 })
3071 .with_color(
3072 style
3073 .indicator
3074 .style_for(
3075 mouse_state,
3076 fold_status == FoldStatus::Folded,
3077 )
3078 .color,
3079 )
3080 .constrained()
3081 .with_width(gutter_margin * style.icon_margin_scale)
3082 .aligned()
3083 .constrained()
3084 .with_height(line_height)
3085 .with_width(gutter_margin)
3086 .aligned()
3087 .boxed()
3088 },
3089 )
3090 .with_cursor_style(CursorStyle::PointingHand)
3091 .with_padding(Padding::uniform(3.))
3092 .on_click(MouseButton::Left, {
3093 move |_, cx| {
3094 cx.dispatch_any_action(match fold_status {
3095 FoldStatus::Folded => Box::new(UnfoldAt { buffer_row }),
3096 FoldStatus::Foldable => Box::new(FoldAt { buffer_row }),
3097 });
3098 }
3099 })
3100 .boxed()
3101 })
3102 })
3103 .flatten()
3104 })
3105 .collect()
3106 }
3107
3108 pub fn context_menu_visible(&self) -> bool {
3109 self.context_menu
3110 .as_ref()
3111 .map_or(false, |menu| menu.visible())
3112 }
3113
3114 pub fn render_context_menu(
3115 &self,
3116 cursor_position: DisplayPoint,
3117 style: EditorStyle,
3118 cx: &mut RenderContext<Editor>,
3119 ) -> Option<(DisplayPoint, ElementBox)> {
3120 self.context_menu
3121 .as_ref()
3122 .map(|menu| menu.render(cursor_position, style, cx))
3123 }
3124
3125 fn show_context_menu(&mut self, menu: ContextMenu, cx: &mut ViewContext<Self>) {
3126 if !matches!(menu, ContextMenu::Completions(_)) {
3127 self.completion_tasks.clear();
3128 }
3129 self.context_menu = Some(menu);
3130 self.hide_copilot_suggestion(cx);
3131 cx.notify();
3132 }
3133
3134 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
3135 cx.notify();
3136 self.completion_tasks.clear();
3137 let context_menu = self.context_menu.take();
3138 if context_menu.is_some() {
3139 self.update_visible_copilot_suggestion(cx);
3140 }
3141 context_menu
3142 }
3143
3144 pub fn insert_snippet(
3145 &mut self,
3146 insertion_ranges: &[Range<usize>],
3147 snippet: Snippet,
3148 cx: &mut ViewContext<Self>,
3149 ) -> Result<()> {
3150 let tabstops = self.buffer.update(cx, |buffer, cx| {
3151 let snippet_text: Arc<str> = snippet.text.clone().into();
3152 buffer.edit(
3153 insertion_ranges
3154 .iter()
3155 .cloned()
3156 .map(|range| (range, snippet_text.clone())),
3157 Some(AutoindentMode::EachLine),
3158 cx,
3159 );
3160
3161 let snapshot = &*buffer.read(cx);
3162 let snippet = &snippet;
3163 snippet
3164 .tabstops
3165 .iter()
3166 .map(|tabstop| {
3167 let mut tabstop_ranges = tabstop
3168 .iter()
3169 .flat_map(|tabstop_range| {
3170 let mut delta = 0_isize;
3171 insertion_ranges.iter().map(move |insertion_range| {
3172 let insertion_start = insertion_range.start as isize + delta;
3173 delta +=
3174 snippet.text.len() as isize - insertion_range.len() as isize;
3175
3176 let start = snapshot.anchor_before(
3177 (insertion_start + tabstop_range.start) as usize,
3178 );
3179 let end = snapshot
3180 .anchor_after((insertion_start + tabstop_range.end) as usize);
3181 start..end
3182 })
3183 })
3184 .collect::<Vec<_>>();
3185 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
3186 tabstop_ranges
3187 })
3188 .collect::<Vec<_>>()
3189 });
3190
3191 if let Some(tabstop) = tabstops.first() {
3192 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
3193 s.select_ranges(tabstop.iter().cloned());
3194 });
3195 self.snippet_stack.push(SnippetState {
3196 active_index: 0,
3197 ranges: tabstops,
3198 });
3199 }
3200
3201 Ok(())
3202 }
3203
3204 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
3205 self.move_to_snippet_tabstop(Bias::Right, cx)
3206 }
3207
3208 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
3209 self.move_to_snippet_tabstop(Bias::Left, cx)
3210 }
3211
3212 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
3213 if let Some(mut snippet) = self.snippet_stack.pop() {
3214 match bias {
3215 Bias::Left => {
3216 if snippet.active_index > 0 {
3217 snippet.active_index -= 1;
3218 } else {
3219 self.snippet_stack.push(snippet);
3220 return false;
3221 }
3222 }
3223 Bias::Right => {
3224 if snippet.active_index + 1 < snippet.ranges.len() {
3225 snippet.active_index += 1;
3226 } else {
3227 self.snippet_stack.push(snippet);
3228 return false;
3229 }
3230 }
3231 }
3232 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
3233 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
3234 s.select_anchor_ranges(current_ranges.iter().cloned())
3235 });
3236 // If snippet state is not at the last tabstop, push it back on the stack
3237 if snippet.active_index + 1 < snippet.ranges.len() {
3238 self.snippet_stack.push(snippet);
3239 }
3240 return true;
3241 }
3242 }
3243
3244 false
3245 }
3246
3247 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
3248 self.transact(cx, |this, cx| {
3249 this.select_all(&SelectAll, cx);
3250 this.insert("", cx);
3251 });
3252 }
3253
3254 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
3255 self.transact(cx, |this, cx| {
3256 this.select_autoclose_pair(cx);
3257 let mut selections = this.selections.all::<Point>(cx);
3258 if !this.selections.line_mode {
3259 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
3260 for selection in &mut selections {
3261 if selection.is_empty() {
3262 let old_head = selection.head();
3263 let mut new_head =
3264 movement::left(&display_map, old_head.to_display_point(&display_map))
3265 .to_point(&display_map);
3266 if let Some((buffer, line_buffer_range)) = display_map
3267 .buffer_snapshot
3268 .buffer_line_for_row(old_head.row)
3269 {
3270 let indent_size =
3271 buffer.indent_size_for_line(line_buffer_range.start.row);
3272 let language_name = buffer
3273 .language_at(line_buffer_range.start)
3274 .map(|language| language.name());
3275 let indent_len = match indent_size.kind {
3276 IndentKind::Space => {
3277 cx.global::<Settings>().tab_size(language_name.as_deref())
3278 }
3279 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
3280 };
3281 if old_head.column <= indent_size.len && old_head.column > 0 {
3282 let indent_len = indent_len.get();
3283 new_head = cmp::min(
3284 new_head,
3285 Point::new(
3286 old_head.row,
3287 ((old_head.column - 1) / indent_len) * indent_len,
3288 ),
3289 );
3290 }
3291 }
3292
3293 selection.set_head(new_head, SelectionGoal::None);
3294 }
3295 }
3296 }
3297
3298 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3299 this.insert("", cx);
3300 this.refresh_copilot_suggestions(cx);
3301 });
3302 }
3303
3304 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
3305 self.transact(cx, |this, cx| {
3306 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3307 let line_mode = s.line_mode;
3308 s.move_with(|map, selection| {
3309 if selection.is_empty() && !line_mode {
3310 let cursor = movement::right(map, selection.head());
3311 selection.set_head(cursor, SelectionGoal::None);
3312 }
3313 })
3314 });
3315 this.insert("", cx);
3316 this.refresh_copilot_suggestions(cx);
3317 });
3318 }
3319
3320 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
3321 if self.move_to_prev_snippet_tabstop(cx) {
3322 return;
3323 }
3324
3325 self.outdent(&Outdent, cx);
3326 }
3327
3328 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
3329 if self.move_to_next_snippet_tabstop(cx) {
3330 return;
3331 }
3332
3333 let mut selections = self.selections.all_adjusted(cx);
3334 let buffer = self.buffer.read(cx);
3335 let snapshot = buffer.snapshot(cx);
3336 let rows_iter = selections.iter().map(|s| s.head().row);
3337 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
3338
3339 let mut edits = Vec::new();
3340 let mut prev_edited_row = 0;
3341 let mut row_delta = 0;
3342 for selection in &mut selections {
3343 if selection.start.row != prev_edited_row {
3344 row_delta = 0;
3345 }
3346 prev_edited_row = selection.end.row;
3347
3348 // If the selection is non-empty, then increase the indentation of the selected lines.
3349 if !selection.is_empty() {
3350 row_delta =
3351 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3352 continue;
3353 }
3354
3355 // If the selection is empty and the cursor is in the leading whitespace before the
3356 // suggested indentation, then auto-indent the line.
3357 let cursor = selection.head();
3358 let current_indent = snapshot.indent_size_for_line(cursor.row);
3359 if let Some(suggested_indent) = suggested_indents.get(&cursor.row).copied() {
3360 if cursor.column < suggested_indent.len
3361 && cursor.column <= current_indent.len
3362 && current_indent.len <= suggested_indent.len
3363 {
3364 selection.start = Point::new(cursor.row, suggested_indent.len);
3365 selection.end = selection.start;
3366 if row_delta == 0 {
3367 edits.extend(Buffer::edit_for_indent_size_adjustment(
3368 cursor.row,
3369 current_indent,
3370 suggested_indent,
3371 ));
3372 row_delta = suggested_indent.len - current_indent.len;
3373 }
3374 continue;
3375 }
3376 }
3377
3378 // Accept copilot suggestion if there is only one selection and the cursor is not
3379 // in the leading whitespace.
3380 if self.selections.count() == 1
3381 && cursor.column >= current_indent.len
3382 && self.has_active_copilot_suggestion(cx)
3383 {
3384 self.accept_copilot_suggestion(cx);
3385 return;
3386 }
3387
3388 // Otherwise, insert a hard or soft tab.
3389 let settings = cx.global::<Settings>();
3390 let language_name = buffer.language_at(cursor, cx).map(|l| l.name());
3391 let tab_size = if settings.hard_tabs(language_name.as_deref()) {
3392 IndentSize::tab()
3393 } else {
3394 let tab_size = settings.tab_size(language_name.as_deref()).get();
3395 let char_column = snapshot
3396 .text_for_range(Point::new(cursor.row, 0)..cursor)
3397 .flat_map(str::chars)
3398 .count()
3399 + row_delta as usize;
3400 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
3401 IndentSize::spaces(chars_to_next_tab_stop)
3402 };
3403 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
3404 selection.end = selection.start;
3405 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
3406 row_delta += tab_size.len;
3407 }
3408
3409 self.transact(cx, |this, cx| {
3410 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3411 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3412 this.refresh_copilot_suggestions(cx);
3413 });
3414 }
3415
3416 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
3417 let mut selections = self.selections.all::<Point>(cx);
3418 let mut prev_edited_row = 0;
3419 let mut row_delta = 0;
3420 let mut edits = Vec::new();
3421 let buffer = self.buffer.read(cx);
3422 let snapshot = buffer.snapshot(cx);
3423 for selection in &mut selections {
3424 if selection.start.row != prev_edited_row {
3425 row_delta = 0;
3426 }
3427 prev_edited_row = selection.end.row;
3428
3429 row_delta =
3430 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3431 }
3432
3433 self.transact(cx, |this, cx| {
3434 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3435 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3436 });
3437 }
3438
3439 fn indent_selection(
3440 buffer: &MultiBuffer,
3441 snapshot: &MultiBufferSnapshot,
3442 selection: &mut Selection<Point>,
3443 edits: &mut Vec<(Range<Point>, String)>,
3444 delta_for_start_row: u32,
3445 cx: &AppContext,
3446 ) -> u32 {
3447 let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
3448 let settings = cx.global::<Settings>();
3449 let tab_size = settings.tab_size(language_name.as_deref()).get();
3450 let indent_kind = if settings.hard_tabs(language_name.as_deref()) {
3451 IndentKind::Tab
3452 } else {
3453 IndentKind::Space
3454 };
3455 let mut start_row = selection.start.row;
3456 let mut end_row = selection.end.row + 1;
3457
3458 // If a selection ends at the beginning of a line, don't indent
3459 // that last line.
3460 if selection.end.column == 0 {
3461 end_row -= 1;
3462 }
3463
3464 // Avoid re-indenting a row that has already been indented by a
3465 // previous selection, but still update this selection's column
3466 // to reflect that indentation.
3467 if delta_for_start_row > 0 {
3468 start_row += 1;
3469 selection.start.column += delta_for_start_row;
3470 if selection.end.row == selection.start.row {
3471 selection.end.column += delta_for_start_row;
3472 }
3473 }
3474
3475 let mut delta_for_end_row = 0;
3476 for row in start_row..end_row {
3477 let current_indent = snapshot.indent_size_for_line(row);
3478 let indent_delta = match (current_indent.kind, indent_kind) {
3479 (IndentKind::Space, IndentKind::Space) => {
3480 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
3481 IndentSize::spaces(columns_to_next_tab_stop)
3482 }
3483 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
3484 (_, IndentKind::Tab) => IndentSize::tab(),
3485 };
3486
3487 let row_start = Point::new(row, 0);
3488 edits.push((
3489 row_start..row_start,
3490 indent_delta.chars().collect::<String>(),
3491 ));
3492
3493 // Update this selection's endpoints to reflect the indentation.
3494 if row == selection.start.row {
3495 selection.start.column += indent_delta.len;
3496 }
3497 if row == selection.end.row {
3498 selection.end.column += indent_delta.len;
3499 delta_for_end_row = indent_delta.len;
3500 }
3501 }
3502
3503 if selection.start.row == selection.end.row {
3504 delta_for_start_row + delta_for_end_row
3505 } else {
3506 delta_for_end_row
3507 }
3508 }
3509
3510 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
3511 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3512 let selections = self.selections.all::<Point>(cx);
3513 let mut deletion_ranges = Vec::new();
3514 let mut last_outdent = None;
3515 {
3516 let buffer = self.buffer.read(cx);
3517 let snapshot = buffer.snapshot(cx);
3518 for selection in &selections {
3519 let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
3520 let tab_size = cx
3521 .global::<Settings>()
3522 .tab_size(language_name.as_deref())
3523 .get();
3524 let mut rows = selection.spanned_rows(false, &display_map);
3525
3526 // Avoid re-outdenting a row that has already been outdented by a
3527 // previous selection.
3528 if let Some(last_row) = last_outdent {
3529 if last_row == rows.start {
3530 rows.start += 1;
3531 }
3532 }
3533
3534 for row in rows {
3535 let indent_size = snapshot.indent_size_for_line(row);
3536 if indent_size.len > 0 {
3537 let deletion_len = match indent_size.kind {
3538 IndentKind::Space => {
3539 let columns_to_prev_tab_stop = indent_size.len % tab_size;
3540 if columns_to_prev_tab_stop == 0 {
3541 tab_size
3542 } else {
3543 columns_to_prev_tab_stop
3544 }
3545 }
3546 IndentKind::Tab => 1,
3547 };
3548 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
3549 last_outdent = Some(row);
3550 }
3551 }
3552 }
3553 }
3554
3555 self.transact(cx, |this, cx| {
3556 this.buffer.update(cx, |buffer, cx| {
3557 let empty_str: Arc<str> = "".into();
3558 buffer.edit(
3559 deletion_ranges
3560 .into_iter()
3561 .map(|range| (range, empty_str.clone())),
3562 None,
3563 cx,
3564 );
3565 });
3566 let selections = this.selections.all::<usize>(cx);
3567 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3568 });
3569 }
3570
3571 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
3572 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3573 let selections = self.selections.all::<Point>(cx);
3574
3575 let mut new_cursors = Vec::new();
3576 let mut edit_ranges = Vec::new();
3577 let mut selections = selections.iter().peekable();
3578 while let Some(selection) = selections.next() {
3579 let mut rows = selection.spanned_rows(false, &display_map);
3580 let goal_display_column = selection.head().to_display_point(&display_map).column();
3581
3582 // Accumulate contiguous regions of rows that we want to delete.
3583 while let Some(next_selection) = selections.peek() {
3584 let next_rows = next_selection.spanned_rows(false, &display_map);
3585 if next_rows.start <= rows.end {
3586 rows.end = next_rows.end;
3587 selections.next().unwrap();
3588 } else {
3589 break;
3590 }
3591 }
3592
3593 let buffer = &display_map.buffer_snapshot;
3594 let mut edit_start = Point::new(rows.start, 0).to_offset(buffer);
3595 let edit_end;
3596 let cursor_buffer_row;
3597 if buffer.max_point().row >= rows.end {
3598 // If there's a line after the range, delete the \n from the end of the row range
3599 // and position the cursor on the next line.
3600 edit_end = Point::new(rows.end, 0).to_offset(buffer);
3601 cursor_buffer_row = rows.end;
3602 } else {
3603 // If there isn't a line after the range, delete the \n from the line before the
3604 // start of the row range and position the cursor there.
3605 edit_start = edit_start.saturating_sub(1);
3606 edit_end = buffer.len();
3607 cursor_buffer_row = rows.start.saturating_sub(1);
3608 }
3609
3610 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
3611 *cursor.column_mut() =
3612 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
3613
3614 new_cursors.push((
3615 selection.id,
3616 buffer.anchor_after(cursor.to_point(&display_map)),
3617 ));
3618 edit_ranges.push(edit_start..edit_end);
3619 }
3620
3621 self.transact(cx, |this, cx| {
3622 let buffer = this.buffer.update(cx, |buffer, cx| {
3623 let empty_str: Arc<str> = "".into();
3624 buffer.edit(
3625 edit_ranges
3626 .into_iter()
3627 .map(|range| (range, empty_str.clone())),
3628 None,
3629 cx,
3630 );
3631 buffer.snapshot(cx)
3632 });
3633 let new_selections = new_cursors
3634 .into_iter()
3635 .map(|(id, cursor)| {
3636 let cursor = cursor.to_point(&buffer);
3637 Selection {
3638 id,
3639 start: cursor,
3640 end: cursor,
3641 reversed: false,
3642 goal: SelectionGoal::None,
3643 }
3644 })
3645 .collect();
3646
3647 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3648 s.select(new_selections);
3649 });
3650 });
3651 }
3652
3653 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
3654 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3655 let buffer = &display_map.buffer_snapshot;
3656 let selections = self.selections.all::<Point>(cx);
3657
3658 let mut edits = Vec::new();
3659 let mut selections_iter = selections.iter().peekable();
3660 while let Some(selection) = selections_iter.next() {
3661 // Avoid duplicating the same lines twice.
3662 let mut rows = selection.spanned_rows(false, &display_map);
3663
3664 while let Some(next_selection) = selections_iter.peek() {
3665 let next_rows = next_selection.spanned_rows(false, &display_map);
3666 if next_rows.start < rows.end {
3667 rows.end = next_rows.end;
3668 selections_iter.next().unwrap();
3669 } else {
3670 break;
3671 }
3672 }
3673
3674 // Copy the text from the selected row region and splice it at the start of the region.
3675 let start = Point::new(rows.start, 0);
3676 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
3677 let text = buffer
3678 .text_for_range(start..end)
3679 .chain(Some("\n"))
3680 .collect::<String>();
3681 edits.push((start..start, text));
3682 }
3683
3684 self.transact(cx, |this, cx| {
3685 this.buffer.update(cx, |buffer, cx| {
3686 buffer.edit(edits, None, cx);
3687 });
3688
3689 this.request_autoscroll(Autoscroll::fit(), cx);
3690 });
3691 }
3692
3693 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
3694 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3695 let buffer = self.buffer.read(cx).snapshot(cx);
3696
3697 let mut edits = Vec::new();
3698 let mut unfold_ranges = Vec::new();
3699 let mut refold_ranges = Vec::new();
3700
3701 let selections = self.selections.all::<Point>(cx);
3702 let mut selections = selections.iter().peekable();
3703 let mut contiguous_row_selections = Vec::new();
3704 let mut new_selections = Vec::new();
3705
3706 while let Some(selection) = selections.next() {
3707 // Find all the selections that span a contiguous row range
3708 let (start_row, end_row) = consume_contiguous_rows(
3709 &mut contiguous_row_selections,
3710 selection,
3711 &display_map,
3712 &mut selections,
3713 );
3714
3715 // Move the text spanned by the row range to be before the line preceding the row range
3716 if start_row > 0 {
3717 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
3718 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
3719 let insertion_point = display_map
3720 .prev_line_boundary(Point::new(start_row - 1, 0))
3721 .0;
3722
3723 // Don't move lines across excerpts
3724 if buffer
3725 .excerpt_boundaries_in_range((
3726 Bound::Excluded(insertion_point),
3727 Bound::Included(range_to_move.end),
3728 ))
3729 .next()
3730 .is_none()
3731 {
3732 let text = buffer
3733 .text_for_range(range_to_move.clone())
3734 .flat_map(|s| s.chars())
3735 .skip(1)
3736 .chain(['\n'])
3737 .collect::<String>();
3738
3739 edits.push((
3740 buffer.anchor_after(range_to_move.start)
3741 ..buffer.anchor_before(range_to_move.end),
3742 String::new(),
3743 ));
3744 let insertion_anchor = buffer.anchor_after(insertion_point);
3745 edits.push((insertion_anchor..insertion_anchor, text));
3746
3747 let row_delta = range_to_move.start.row - insertion_point.row + 1;
3748
3749 // Move selections up
3750 new_selections.extend(contiguous_row_selections.drain(..).map(
3751 |mut selection| {
3752 selection.start.row -= row_delta;
3753 selection.end.row -= row_delta;
3754 selection
3755 },
3756 ));
3757
3758 // Move folds up
3759 unfold_ranges.push(range_to_move.clone());
3760 for fold in display_map.folds_in_range(
3761 buffer.anchor_before(range_to_move.start)
3762 ..buffer.anchor_after(range_to_move.end),
3763 ) {
3764 let mut start = fold.start.to_point(&buffer);
3765 let mut end = fold.end.to_point(&buffer);
3766 start.row -= row_delta;
3767 end.row -= row_delta;
3768 refold_ranges.push(start..end);
3769 }
3770 }
3771 }
3772
3773 // If we didn't move line(s), preserve the existing selections
3774 new_selections.append(&mut contiguous_row_selections);
3775 }
3776
3777 self.transact(cx, |this, cx| {
3778 this.unfold_ranges(unfold_ranges, true, true, cx);
3779 this.buffer.update(cx, |buffer, cx| {
3780 for (range, text) in edits {
3781 buffer.edit([(range, text)], None, cx);
3782 }
3783 });
3784 this.fold_ranges(refold_ranges, true, cx);
3785 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3786 s.select(new_selections);
3787 })
3788 });
3789 }
3790
3791 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
3792 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3793 let buffer = self.buffer.read(cx).snapshot(cx);
3794
3795 let mut edits = Vec::new();
3796 let mut unfold_ranges = Vec::new();
3797 let mut refold_ranges = Vec::new();
3798
3799 let selections = self.selections.all::<Point>(cx);
3800 let mut selections = selections.iter().peekable();
3801 let mut contiguous_row_selections = Vec::new();
3802 let mut new_selections = Vec::new();
3803
3804 while let Some(selection) = selections.next() {
3805 // Find all the selections that span a contiguous row range
3806 let (start_row, end_row) = consume_contiguous_rows(
3807 &mut contiguous_row_selections,
3808 selection,
3809 &display_map,
3810 &mut selections,
3811 );
3812
3813 // Move the text spanned by the row range to be after the last line of the row range
3814 if end_row <= buffer.max_point().row {
3815 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
3816 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
3817
3818 // Don't move lines across excerpt boundaries
3819 if buffer
3820 .excerpt_boundaries_in_range((
3821 Bound::Excluded(range_to_move.start),
3822 Bound::Included(insertion_point),
3823 ))
3824 .next()
3825 .is_none()
3826 {
3827 let mut text = String::from("\n");
3828 text.extend(buffer.text_for_range(range_to_move.clone()));
3829 text.pop(); // Drop trailing newline
3830 edits.push((
3831 buffer.anchor_after(range_to_move.start)
3832 ..buffer.anchor_before(range_to_move.end),
3833 String::new(),
3834 ));
3835 let insertion_anchor = buffer.anchor_after(insertion_point);
3836 edits.push((insertion_anchor..insertion_anchor, text));
3837
3838 let row_delta = insertion_point.row - range_to_move.end.row + 1;
3839
3840 // Move selections down
3841 new_selections.extend(contiguous_row_selections.drain(..).map(
3842 |mut selection| {
3843 selection.start.row += row_delta;
3844 selection.end.row += row_delta;
3845 selection
3846 },
3847 ));
3848
3849 // Move folds down
3850 unfold_ranges.push(range_to_move.clone());
3851 for fold in display_map.folds_in_range(
3852 buffer.anchor_before(range_to_move.start)
3853 ..buffer.anchor_after(range_to_move.end),
3854 ) {
3855 let mut start = fold.start.to_point(&buffer);
3856 let mut end = fold.end.to_point(&buffer);
3857 start.row += row_delta;
3858 end.row += row_delta;
3859 refold_ranges.push(start..end);
3860 }
3861 }
3862 }
3863
3864 // If we didn't move line(s), preserve the existing selections
3865 new_selections.append(&mut contiguous_row_selections);
3866 }
3867
3868 self.transact(cx, |this, cx| {
3869 this.unfold_ranges(unfold_ranges, true, true, cx);
3870 this.buffer.update(cx, |buffer, cx| {
3871 for (range, text) in edits {
3872 buffer.edit([(range, text)], None, cx);
3873 }
3874 });
3875 this.fold_ranges(refold_ranges, true, cx);
3876 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
3877 });
3878 }
3879
3880 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
3881 self.transact(cx, |this, cx| {
3882 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3883 let mut edits: Vec<(Range<usize>, String)> = Default::default();
3884 let line_mode = s.line_mode;
3885 s.move_with(|display_map, selection| {
3886 if !selection.is_empty() || line_mode {
3887 return;
3888 }
3889
3890 let mut head = selection.head();
3891 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
3892 if head.column() == display_map.line_len(head.row()) {
3893 transpose_offset = display_map
3894 .buffer_snapshot
3895 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
3896 }
3897
3898 if transpose_offset == 0 {
3899 return;
3900 }
3901
3902 *head.column_mut() += 1;
3903 head = display_map.clip_point(head, Bias::Right);
3904 selection.collapse_to(head, SelectionGoal::Column(head.column()));
3905
3906 let transpose_start = display_map
3907 .buffer_snapshot
3908 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
3909 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
3910 let transpose_end = display_map
3911 .buffer_snapshot
3912 .clip_offset(transpose_offset + 1, Bias::Right);
3913 if let Some(ch) =
3914 display_map.buffer_snapshot.chars_at(transpose_start).next()
3915 {
3916 edits.push((transpose_start..transpose_offset, String::new()));
3917 edits.push((transpose_end..transpose_end, ch.to_string()));
3918 }
3919 }
3920 });
3921 edits
3922 });
3923 this.buffer
3924 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
3925 let selections = this.selections.all::<usize>(cx);
3926 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3927 s.select(selections);
3928 });
3929 });
3930 }
3931
3932 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
3933 let mut text = String::new();
3934 let buffer = self.buffer.read(cx).snapshot(cx);
3935 let mut selections = self.selections.all::<Point>(cx);
3936 let mut clipboard_selections = Vec::with_capacity(selections.len());
3937 {
3938 let max_point = buffer.max_point();
3939 for selection in &mut selections {
3940 let is_entire_line = selection.is_empty() || self.selections.line_mode;
3941 if is_entire_line {
3942 selection.start = Point::new(selection.start.row, 0);
3943 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
3944 selection.goal = SelectionGoal::None;
3945 }
3946 let mut len = 0;
3947 for chunk in buffer.text_for_range(selection.start..selection.end) {
3948 text.push_str(chunk);
3949 len += chunk.len();
3950 }
3951 clipboard_selections.push(ClipboardSelection {
3952 len,
3953 is_entire_line,
3954 first_line_indent: buffer.indent_size_for_line(selection.start.row).len,
3955 });
3956 }
3957 }
3958
3959 self.transact(cx, |this, cx| {
3960 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3961 s.select(selections);
3962 });
3963 this.insert("", cx);
3964 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
3965 });
3966 }
3967
3968 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
3969 let selections = self.selections.all::<Point>(cx);
3970 let buffer = self.buffer.read(cx).read(cx);
3971 let mut text = String::new();
3972
3973 let mut clipboard_selections = Vec::with_capacity(selections.len());
3974 {
3975 let max_point = buffer.max_point();
3976 for selection in selections.iter() {
3977 let mut start = selection.start;
3978 let mut end = selection.end;
3979 let is_entire_line = selection.is_empty() || self.selections.line_mode;
3980 if is_entire_line {
3981 start = Point::new(start.row, 0);
3982 end = cmp::min(max_point, Point::new(end.row + 1, 0));
3983 }
3984 let mut len = 0;
3985 for chunk in buffer.text_for_range(start..end) {
3986 text.push_str(chunk);
3987 len += chunk.len();
3988 }
3989 clipboard_selections.push(ClipboardSelection {
3990 len,
3991 is_entire_line,
3992 first_line_indent: buffer.indent_size_for_line(start.row).len,
3993 });
3994 }
3995 }
3996
3997 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
3998 }
3999
4000 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
4001 self.transact(cx, |this, cx| {
4002 if let Some(item) = cx.as_mut().read_from_clipboard() {
4003 let mut clipboard_text = Cow::Borrowed(item.text());
4004 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
4005 let old_selections = this.selections.all::<usize>(cx);
4006 let all_selections_were_entire_line =
4007 clipboard_selections.iter().all(|s| s.is_entire_line);
4008 let first_selection_indent_column =
4009 clipboard_selections.first().map(|s| s.first_line_indent);
4010 if clipboard_selections.len() != old_selections.len() {
4011 let mut newline_separated_text = String::new();
4012 let mut clipboard_selections = clipboard_selections.drain(..).peekable();
4013 let mut ix = 0;
4014 while let Some(clipboard_selection) = clipboard_selections.next() {
4015 newline_separated_text
4016 .push_str(&clipboard_text[ix..ix + clipboard_selection.len]);
4017 ix += clipboard_selection.len;
4018 if clipboard_selections.peek().is_some() {
4019 newline_separated_text.push('\n');
4020 }
4021 }
4022 clipboard_text = Cow::Owned(newline_separated_text);
4023 }
4024
4025 this.buffer.update(cx, |buffer, cx| {
4026 let snapshot = buffer.read(cx);
4027 let mut start_offset = 0;
4028 let mut edits = Vec::new();
4029 let mut original_indent_columns = Vec::new();
4030 let line_mode = this.selections.line_mode;
4031 for (ix, selection) in old_selections.iter().enumerate() {
4032 let to_insert;
4033 let entire_line;
4034 let original_indent_column;
4035 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
4036 let end_offset = start_offset + clipboard_selection.len;
4037 to_insert = &clipboard_text[start_offset..end_offset];
4038 entire_line = clipboard_selection.is_entire_line;
4039 start_offset = end_offset;
4040 original_indent_column =
4041 Some(clipboard_selection.first_line_indent);
4042 } else {
4043 to_insert = clipboard_text.as_str();
4044 entire_line = all_selections_were_entire_line;
4045 original_indent_column = first_selection_indent_column
4046 }
4047
4048 // If the corresponding selection was empty when this slice of the
4049 // clipboard text was written, then the entire line containing the
4050 // selection was copied. If this selection is also currently empty,
4051 // then paste the line before the current line of the buffer.
4052 let range = if selection.is_empty() && !line_mode && entire_line {
4053 let column = selection.start.to_point(&snapshot).column as usize;
4054 let line_start = selection.start - column;
4055 line_start..line_start
4056 } else {
4057 selection.range()
4058 };
4059
4060 edits.push((range, to_insert));
4061 original_indent_columns.extend(original_indent_column);
4062 }
4063 drop(snapshot);
4064
4065 buffer.edit(
4066 edits,
4067 Some(AutoindentMode::Block {
4068 original_indent_columns,
4069 }),
4070 cx,
4071 );
4072 });
4073
4074 let selections = this.selections.all::<usize>(cx);
4075 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4076 } else {
4077 this.insert(&clipboard_text, cx);
4078 }
4079 }
4080 });
4081 }
4082
4083 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
4084 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
4085 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
4086 self.change_selections(None, cx, |s| {
4087 s.select_anchors(selections.to_vec());
4088 });
4089 }
4090 self.request_autoscroll(Autoscroll::fit(), cx);
4091 self.unmark_text(cx);
4092 self.refresh_copilot_suggestions(cx);
4093 cx.emit(Event::Edited);
4094 }
4095 }
4096
4097 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
4098 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
4099 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
4100 {
4101 self.change_selections(None, cx, |s| {
4102 s.select_anchors(selections.to_vec());
4103 });
4104 }
4105 self.request_autoscroll(Autoscroll::fit(), cx);
4106 self.unmark_text(cx);
4107 self.refresh_copilot_suggestions(cx);
4108 cx.emit(Event::Edited);
4109 }
4110 }
4111
4112 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
4113 self.buffer
4114 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
4115 }
4116
4117 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
4118 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4119 let line_mode = s.line_mode;
4120 s.move_with(|map, selection| {
4121 let cursor = if selection.is_empty() && !line_mode {
4122 movement::left(map, selection.start)
4123 } else {
4124 selection.start
4125 };
4126 selection.collapse_to(cursor, SelectionGoal::None);
4127 });
4128 })
4129 }
4130
4131 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
4132 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4133 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
4134 })
4135 }
4136
4137 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
4138 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4139 let line_mode = s.line_mode;
4140 s.move_with(|map, selection| {
4141 let cursor = if selection.is_empty() && !line_mode {
4142 movement::right(map, selection.end)
4143 } else {
4144 selection.end
4145 };
4146 selection.collapse_to(cursor, SelectionGoal::None)
4147 });
4148 })
4149 }
4150
4151 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
4152 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4153 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
4154 })
4155 }
4156
4157 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
4158 if self.take_rename(true, cx).is_some() {
4159 return;
4160 }
4161
4162 if let Some(context_menu) = self.context_menu.as_mut() {
4163 if context_menu.select_prev(cx) {
4164 return;
4165 }
4166 }
4167
4168 if matches!(self.mode, EditorMode::SingleLine) {
4169 cx.propagate_action();
4170 return;
4171 }
4172
4173 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4174 let line_mode = s.line_mode;
4175 s.move_with(|map, selection| {
4176 if !selection.is_empty() && !line_mode {
4177 selection.goal = SelectionGoal::None;
4178 }
4179 let (cursor, goal) = movement::up(map, selection.start, selection.goal, false);
4180 selection.collapse_to(cursor, goal);
4181 });
4182 })
4183 }
4184
4185 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
4186 if self.take_rename(true, cx).is_some() {
4187 return;
4188 }
4189
4190 if self
4191 .context_menu
4192 .as_mut()
4193 .map(|menu| menu.select_first(cx))
4194 .unwrap_or(false)
4195 {
4196 return;
4197 }
4198
4199 if matches!(self.mode, EditorMode::SingleLine) {
4200 cx.propagate_action();
4201 return;
4202 }
4203
4204 let row_count = if let Some(row_count) = self.visible_line_count() {
4205 row_count as u32 - 1
4206 } else {
4207 return;
4208 };
4209
4210 let autoscroll = if action.center_cursor {
4211 Autoscroll::center()
4212 } else {
4213 Autoscroll::fit()
4214 };
4215
4216 self.change_selections(Some(autoscroll), cx, |s| {
4217 let line_mode = s.line_mode;
4218 s.move_with(|map, selection| {
4219 if !selection.is_empty() && !line_mode {
4220 selection.goal = SelectionGoal::None;
4221 }
4222 let (cursor, goal) =
4223 movement::up_by_rows(map, selection.end, row_count, selection.goal, false);
4224 selection.collapse_to(cursor, goal);
4225 });
4226 });
4227 }
4228
4229 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
4230 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4231 s.move_heads_with(|map, head, goal| movement::up(map, head, goal, false))
4232 })
4233 }
4234
4235 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
4236 self.take_rename(true, cx);
4237
4238 if let Some(context_menu) = self.context_menu.as_mut() {
4239 if context_menu.select_next(cx) {
4240 return;
4241 }
4242 }
4243
4244 if self.mode == EditorMode::SingleLine {
4245 cx.propagate_action();
4246 return;
4247 }
4248
4249 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4250 let line_mode = s.line_mode;
4251 s.move_with(|map, selection| {
4252 if !selection.is_empty() && !line_mode {
4253 selection.goal = SelectionGoal::None;
4254 }
4255 let (cursor, goal) = movement::down(map, selection.end, selection.goal, false);
4256 selection.collapse_to(cursor, goal);
4257 });
4258 });
4259 }
4260
4261 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
4262 if self.take_rename(true, cx).is_some() {
4263 return;
4264 }
4265
4266 if self
4267 .context_menu
4268 .as_mut()
4269 .map(|menu| menu.select_last(cx))
4270 .unwrap_or(false)
4271 {
4272 return;
4273 }
4274
4275 if matches!(self.mode, EditorMode::SingleLine) {
4276 cx.propagate_action();
4277 return;
4278 }
4279
4280 let row_count = if let Some(row_count) = self.visible_line_count() {
4281 row_count as u32 - 1
4282 } else {
4283 return;
4284 };
4285
4286 let autoscroll = if action.center_cursor {
4287 Autoscroll::center()
4288 } else {
4289 Autoscroll::fit()
4290 };
4291
4292 self.change_selections(Some(autoscroll), cx, |s| {
4293 let line_mode = s.line_mode;
4294 s.move_with(|map, selection| {
4295 if !selection.is_empty() && !line_mode {
4296 selection.goal = SelectionGoal::None;
4297 }
4298 let (cursor, goal) =
4299 movement::down_by_rows(map, selection.end, row_count, selection.goal, false);
4300 selection.collapse_to(cursor, goal);
4301 });
4302 });
4303 }
4304
4305 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
4306 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4307 s.move_heads_with(|map, head, goal| movement::down(map, head, goal, false))
4308 });
4309 }
4310
4311 pub fn move_to_previous_word_start(
4312 &mut self,
4313 _: &MoveToPreviousWordStart,
4314 cx: &mut ViewContext<Self>,
4315 ) {
4316 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4317 s.move_cursors_with(|map, head, _| {
4318 (
4319 movement::previous_word_start(map, head),
4320 SelectionGoal::None,
4321 )
4322 });
4323 })
4324 }
4325
4326 pub fn move_to_previous_subword_start(
4327 &mut self,
4328 _: &MoveToPreviousSubwordStart,
4329 cx: &mut ViewContext<Self>,
4330 ) {
4331 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4332 s.move_cursors_with(|map, head, _| {
4333 (
4334 movement::previous_subword_start(map, head),
4335 SelectionGoal::None,
4336 )
4337 });
4338 })
4339 }
4340
4341 pub fn select_to_previous_word_start(
4342 &mut self,
4343 _: &SelectToPreviousWordStart,
4344 cx: &mut ViewContext<Self>,
4345 ) {
4346 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4347 s.move_heads_with(|map, head, _| {
4348 (
4349 movement::previous_word_start(map, head),
4350 SelectionGoal::None,
4351 )
4352 });
4353 })
4354 }
4355
4356 pub fn select_to_previous_subword_start(
4357 &mut self,
4358 _: &SelectToPreviousSubwordStart,
4359 cx: &mut ViewContext<Self>,
4360 ) {
4361 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4362 s.move_heads_with(|map, head, _| {
4363 (
4364 movement::previous_subword_start(map, head),
4365 SelectionGoal::None,
4366 )
4367 });
4368 })
4369 }
4370
4371 pub fn delete_to_previous_word_start(
4372 &mut self,
4373 _: &DeleteToPreviousWordStart,
4374 cx: &mut ViewContext<Self>,
4375 ) {
4376 self.transact(cx, |this, cx| {
4377 this.select_autoclose_pair(cx);
4378 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4379 let line_mode = s.line_mode;
4380 s.move_with(|map, selection| {
4381 if selection.is_empty() && !line_mode {
4382 let cursor = movement::previous_word_start(map, selection.head());
4383 selection.set_head(cursor, SelectionGoal::None);
4384 }
4385 });
4386 });
4387 this.insert("", cx);
4388 });
4389 }
4390
4391 pub fn delete_to_previous_subword_start(
4392 &mut self,
4393 _: &DeleteToPreviousSubwordStart,
4394 cx: &mut ViewContext<Self>,
4395 ) {
4396 self.transact(cx, |this, cx| {
4397 this.select_autoclose_pair(cx);
4398 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4399 let line_mode = s.line_mode;
4400 s.move_with(|map, selection| {
4401 if selection.is_empty() && !line_mode {
4402 let cursor = movement::previous_subword_start(map, selection.head());
4403 selection.set_head(cursor, SelectionGoal::None);
4404 }
4405 });
4406 });
4407 this.insert("", cx);
4408 });
4409 }
4410
4411 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
4412 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4413 s.move_cursors_with(|map, head, _| {
4414 (movement::next_word_end(map, head), SelectionGoal::None)
4415 });
4416 })
4417 }
4418
4419 pub fn move_to_next_subword_end(
4420 &mut self,
4421 _: &MoveToNextSubwordEnd,
4422 cx: &mut ViewContext<Self>,
4423 ) {
4424 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4425 s.move_cursors_with(|map, head, _| {
4426 (movement::next_subword_end(map, head), SelectionGoal::None)
4427 });
4428 })
4429 }
4430
4431 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
4432 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4433 s.move_heads_with(|map, head, _| {
4434 (movement::next_word_end(map, head), SelectionGoal::None)
4435 });
4436 })
4437 }
4438
4439 pub fn select_to_next_subword_end(
4440 &mut self,
4441 _: &SelectToNextSubwordEnd,
4442 cx: &mut ViewContext<Self>,
4443 ) {
4444 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4445 s.move_heads_with(|map, head, _| {
4446 (movement::next_subword_end(map, head), SelectionGoal::None)
4447 });
4448 })
4449 }
4450
4451 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
4452 self.transact(cx, |this, cx| {
4453 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4454 let line_mode = s.line_mode;
4455 s.move_with(|map, selection| {
4456 if selection.is_empty() && !line_mode {
4457 let cursor = movement::next_word_end(map, selection.head());
4458 selection.set_head(cursor, SelectionGoal::None);
4459 }
4460 });
4461 });
4462 this.insert("", cx);
4463 });
4464 }
4465
4466 pub fn delete_to_next_subword_end(
4467 &mut self,
4468 _: &DeleteToNextSubwordEnd,
4469 cx: &mut ViewContext<Self>,
4470 ) {
4471 self.transact(cx, |this, cx| {
4472 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4473 s.move_with(|map, selection| {
4474 if selection.is_empty() {
4475 let cursor = movement::next_subword_end(map, selection.head());
4476 selection.set_head(cursor, SelectionGoal::None);
4477 }
4478 });
4479 });
4480 this.insert("", cx);
4481 });
4482 }
4483
4484 pub fn move_to_beginning_of_line(
4485 &mut self,
4486 _: &MoveToBeginningOfLine,
4487 cx: &mut ViewContext<Self>,
4488 ) {
4489 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4490 s.move_cursors_with(|map, head, _| {
4491 (
4492 movement::indented_line_beginning(map, head, true),
4493 SelectionGoal::None,
4494 )
4495 });
4496 })
4497 }
4498
4499 pub fn select_to_beginning_of_line(
4500 &mut self,
4501 action: &SelectToBeginningOfLine,
4502 cx: &mut ViewContext<Self>,
4503 ) {
4504 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4505 s.move_heads_with(|map, head, _| {
4506 (
4507 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
4508 SelectionGoal::None,
4509 )
4510 });
4511 });
4512 }
4513
4514 pub fn delete_to_beginning_of_line(
4515 &mut self,
4516 _: &DeleteToBeginningOfLine,
4517 cx: &mut ViewContext<Self>,
4518 ) {
4519 self.transact(cx, |this, cx| {
4520 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4521 s.move_with(|_, selection| {
4522 selection.reversed = true;
4523 });
4524 });
4525
4526 this.select_to_beginning_of_line(
4527 &SelectToBeginningOfLine {
4528 stop_at_soft_wraps: false,
4529 },
4530 cx,
4531 );
4532 this.backspace(&Backspace, cx);
4533 });
4534 }
4535
4536 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
4537 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4538 s.move_cursors_with(|map, head, _| {
4539 (movement::line_end(map, head, true), SelectionGoal::None)
4540 });
4541 })
4542 }
4543
4544 pub fn select_to_end_of_line(
4545 &mut self,
4546 action: &SelectToEndOfLine,
4547 cx: &mut ViewContext<Self>,
4548 ) {
4549 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4550 s.move_heads_with(|map, head, _| {
4551 (
4552 movement::line_end(map, head, action.stop_at_soft_wraps),
4553 SelectionGoal::None,
4554 )
4555 });
4556 })
4557 }
4558
4559 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
4560 self.transact(cx, |this, cx| {
4561 this.select_to_end_of_line(
4562 &SelectToEndOfLine {
4563 stop_at_soft_wraps: false,
4564 },
4565 cx,
4566 );
4567 this.delete(&Delete, cx);
4568 });
4569 }
4570
4571 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
4572 self.transact(cx, |this, cx| {
4573 this.select_to_end_of_line(
4574 &SelectToEndOfLine {
4575 stop_at_soft_wraps: false,
4576 },
4577 cx,
4578 );
4579 this.cut(&Cut, cx);
4580 });
4581 }
4582
4583 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
4584 if matches!(self.mode, EditorMode::SingleLine) {
4585 cx.propagate_action();
4586 return;
4587 }
4588
4589 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4590 s.select_ranges(vec![0..0]);
4591 });
4592 }
4593
4594 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
4595 let mut selection = self.selections.last::<Point>(cx);
4596 selection.set_head(Point::zero(), SelectionGoal::None);
4597
4598 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4599 s.select(vec![selection]);
4600 });
4601 }
4602
4603 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
4604 if matches!(self.mode, EditorMode::SingleLine) {
4605 cx.propagate_action();
4606 return;
4607 }
4608
4609 let cursor = self.buffer.read(cx).read(cx).len();
4610 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4611 s.select_ranges(vec![cursor..cursor])
4612 });
4613 }
4614
4615 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
4616 self.nav_history = nav_history;
4617 }
4618
4619 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
4620 self.nav_history.as_ref()
4621 }
4622
4623 fn push_to_nav_history(
4624 &self,
4625 cursor_anchor: Anchor,
4626 new_position: Option<Point>,
4627 cx: &mut ViewContext<Self>,
4628 ) {
4629 if let Some(nav_history) = &self.nav_history {
4630 let buffer = self.buffer.read(cx).read(cx);
4631 let cursor_position = cursor_anchor.to_point(&buffer);
4632 let scroll_state = self.scroll_manager.anchor();
4633 let scroll_top_row = scroll_state.top_row(&buffer);
4634 drop(buffer);
4635
4636 if let Some(new_position) = new_position {
4637 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
4638 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
4639 return;
4640 }
4641 }
4642
4643 nav_history.push(
4644 Some(NavigationData {
4645 cursor_anchor,
4646 cursor_position,
4647 scroll_anchor: scroll_state,
4648 scroll_top_row,
4649 }),
4650 cx,
4651 );
4652 }
4653 }
4654
4655 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
4656 let buffer = self.buffer.read(cx).snapshot(cx);
4657 let mut selection = self.selections.first::<usize>(cx);
4658 selection.set_head(buffer.len(), SelectionGoal::None);
4659 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4660 s.select(vec![selection]);
4661 });
4662 }
4663
4664 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
4665 let end = self.buffer.read(cx).read(cx).len();
4666 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4667 s.select_ranges(vec![0..end]);
4668 });
4669 }
4670
4671 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
4672 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4673 let mut selections = self.selections.all::<Point>(cx);
4674 let max_point = display_map.buffer_snapshot.max_point();
4675 for selection in &mut selections {
4676 let rows = selection.spanned_rows(true, &display_map);
4677 selection.start = Point::new(rows.start, 0);
4678 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
4679 selection.reversed = false;
4680 }
4681 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4682 s.select(selections);
4683 });
4684 }
4685
4686 pub fn split_selection_into_lines(
4687 &mut self,
4688 _: &SplitSelectionIntoLines,
4689 cx: &mut ViewContext<Self>,
4690 ) {
4691 let mut to_unfold = Vec::new();
4692 let mut new_selection_ranges = Vec::new();
4693 {
4694 let selections = self.selections.all::<Point>(cx);
4695 let buffer = self.buffer.read(cx).read(cx);
4696 for selection in selections {
4697 for row in selection.start.row..selection.end.row {
4698 let cursor = Point::new(row, buffer.line_len(row));
4699 new_selection_ranges.push(cursor..cursor);
4700 }
4701 new_selection_ranges.push(selection.end..selection.end);
4702 to_unfold.push(selection.start..selection.end);
4703 }
4704 }
4705 self.unfold_ranges(to_unfold, true, true, cx);
4706 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4707 s.select_ranges(new_selection_ranges);
4708 });
4709 }
4710
4711 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
4712 self.add_selection(true, cx);
4713 }
4714
4715 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
4716 self.add_selection(false, cx);
4717 }
4718
4719 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
4720 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4721 let mut selections = self.selections.all::<Point>(cx);
4722 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
4723 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
4724 let range = oldest_selection.display_range(&display_map).sorted();
4725 let columns = cmp::min(range.start.column(), range.end.column())
4726 ..cmp::max(range.start.column(), range.end.column());
4727
4728 selections.clear();
4729 let mut stack = Vec::new();
4730 for row in range.start.row()..=range.end.row() {
4731 if let Some(selection) = self.selections.build_columnar_selection(
4732 &display_map,
4733 row,
4734 &columns,
4735 oldest_selection.reversed,
4736 ) {
4737 stack.push(selection.id);
4738 selections.push(selection);
4739 }
4740 }
4741
4742 if above {
4743 stack.reverse();
4744 }
4745
4746 AddSelectionsState { above, stack }
4747 });
4748
4749 let last_added_selection = *state.stack.last().unwrap();
4750 let mut new_selections = Vec::new();
4751 if above == state.above {
4752 let end_row = if above {
4753 0
4754 } else {
4755 display_map.max_point().row()
4756 };
4757
4758 'outer: for selection in selections {
4759 if selection.id == last_added_selection {
4760 let range = selection.display_range(&display_map).sorted();
4761 debug_assert_eq!(range.start.row(), range.end.row());
4762 let mut row = range.start.row();
4763 let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
4764 {
4765 start..end
4766 } else {
4767 cmp::min(range.start.column(), range.end.column())
4768 ..cmp::max(range.start.column(), range.end.column())
4769 };
4770
4771 while row != end_row {
4772 if above {
4773 row -= 1;
4774 } else {
4775 row += 1;
4776 }
4777
4778 if let Some(new_selection) = self.selections.build_columnar_selection(
4779 &display_map,
4780 row,
4781 &columns,
4782 selection.reversed,
4783 ) {
4784 state.stack.push(new_selection.id);
4785 if above {
4786 new_selections.push(new_selection);
4787 new_selections.push(selection);
4788 } else {
4789 new_selections.push(selection);
4790 new_selections.push(new_selection);
4791 }
4792
4793 continue 'outer;
4794 }
4795 }
4796 }
4797
4798 new_selections.push(selection);
4799 }
4800 } else {
4801 new_selections = selections;
4802 new_selections.retain(|s| s.id != last_added_selection);
4803 state.stack.pop();
4804 }
4805
4806 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4807 s.select(new_selections);
4808 });
4809 if state.stack.len() > 1 {
4810 self.add_selections_state = Some(state);
4811 }
4812 }
4813
4814 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) {
4815 self.push_to_selection_history();
4816 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4817 let buffer = &display_map.buffer_snapshot;
4818 let mut selections = self.selections.all::<usize>(cx);
4819 if let Some(mut select_next_state) = self.select_next_state.take() {
4820 let query = &select_next_state.query;
4821 if !select_next_state.done {
4822 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
4823 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
4824 let mut next_selected_range = None;
4825
4826 let bytes_after_last_selection =
4827 buffer.bytes_in_range(last_selection.end..buffer.len());
4828 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
4829 let query_matches = query
4830 .stream_find_iter(bytes_after_last_selection)
4831 .map(|result| (last_selection.end, result))
4832 .chain(
4833 query
4834 .stream_find_iter(bytes_before_first_selection)
4835 .map(|result| (0, result)),
4836 );
4837 for (start_offset, query_match) in query_matches {
4838 let query_match = query_match.unwrap(); // can only fail due to I/O
4839 let offset_range =
4840 start_offset + query_match.start()..start_offset + query_match.end();
4841 let display_range = offset_range.start.to_display_point(&display_map)
4842 ..offset_range.end.to_display_point(&display_map);
4843
4844 if !select_next_state.wordwise
4845 || (!movement::is_inside_word(&display_map, display_range.start)
4846 && !movement::is_inside_word(&display_map, display_range.end))
4847 {
4848 next_selected_range = Some(offset_range);
4849 break;
4850 }
4851 }
4852
4853 if let Some(next_selected_range) = next_selected_range {
4854 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
4855 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
4856 if action.replace_newest {
4857 s.delete(s.newest_anchor().id);
4858 }
4859 s.insert_range(next_selected_range);
4860 });
4861 } else {
4862 select_next_state.done = true;
4863 }
4864 }
4865
4866 self.select_next_state = Some(select_next_state);
4867 } else if selections.len() == 1 {
4868 let selection = selections.last_mut().unwrap();
4869 if selection.start == selection.end {
4870 let word_range = movement::surrounding_word(
4871 &display_map,
4872 selection.start.to_display_point(&display_map),
4873 );
4874 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
4875 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
4876 selection.goal = SelectionGoal::None;
4877 selection.reversed = false;
4878
4879 let query = buffer
4880 .text_for_range(selection.start..selection.end)
4881 .collect::<String>();
4882 let select_state = SelectNextState {
4883 query: AhoCorasick::new_auto_configured(&[query]),
4884 wordwise: true,
4885 done: false,
4886 };
4887 self.unfold_ranges([selection.start..selection.end], false, true, cx);
4888 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
4889 s.select(selections);
4890 });
4891 self.select_next_state = Some(select_state);
4892 } else {
4893 let query = buffer
4894 .text_for_range(selection.start..selection.end)
4895 .collect::<String>();
4896 self.select_next_state = Some(SelectNextState {
4897 query: AhoCorasick::new_auto_configured(&[query]),
4898 wordwise: false,
4899 done: false,
4900 });
4901 self.select_next(action, cx);
4902 }
4903 }
4904 }
4905
4906 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
4907 self.transact(cx, |this, cx| {
4908 let mut selections = this.selections.all::<Point>(cx);
4909 let mut edits = Vec::new();
4910 let mut selection_edit_ranges = Vec::new();
4911 let mut last_toggled_row = None;
4912 let snapshot = this.buffer.read(cx).read(cx);
4913 let empty_str: Arc<str> = "".into();
4914 let mut suffixes_inserted = Vec::new();
4915
4916 fn comment_prefix_range(
4917 snapshot: &MultiBufferSnapshot,
4918 row: u32,
4919 comment_prefix: &str,
4920 comment_prefix_whitespace: &str,
4921 ) -> Range<Point> {
4922 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
4923
4924 let mut line_bytes = snapshot
4925 .bytes_in_range(start..snapshot.max_point())
4926 .flatten()
4927 .copied();
4928
4929 // If this line currently begins with the line comment prefix, then record
4930 // the range containing the prefix.
4931 if line_bytes
4932 .by_ref()
4933 .take(comment_prefix.len())
4934 .eq(comment_prefix.bytes())
4935 {
4936 // Include any whitespace that matches the comment prefix.
4937 let matching_whitespace_len = line_bytes
4938 .zip(comment_prefix_whitespace.bytes())
4939 .take_while(|(a, b)| a == b)
4940 .count() as u32;
4941 let end = Point::new(
4942 start.row,
4943 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
4944 );
4945 start..end
4946 } else {
4947 start..start
4948 }
4949 }
4950
4951 fn comment_suffix_range(
4952 snapshot: &MultiBufferSnapshot,
4953 row: u32,
4954 comment_suffix: &str,
4955 comment_suffix_has_leading_space: bool,
4956 ) -> Range<Point> {
4957 let end = Point::new(row, snapshot.line_len(row));
4958 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
4959
4960 let mut line_end_bytes = snapshot
4961 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
4962 .flatten()
4963 .copied();
4964
4965 let leading_space_len = if suffix_start_column > 0
4966 && line_end_bytes.next() == Some(b' ')
4967 && comment_suffix_has_leading_space
4968 {
4969 1
4970 } else {
4971 0
4972 };
4973
4974 // If this line currently begins with the line comment prefix, then record
4975 // the range containing the prefix.
4976 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
4977 let start = Point::new(end.row, suffix_start_column - leading_space_len);
4978 start..end
4979 } else {
4980 end..end
4981 }
4982 }
4983
4984 // TODO: Handle selections that cross excerpts
4985 for selection in &mut selections {
4986 let start_column = snapshot.indent_size_for_line(selection.start.row).len;
4987 let language = if let Some(language) =
4988 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
4989 {
4990 language
4991 } else {
4992 continue;
4993 };
4994
4995 selection_edit_ranges.clear();
4996
4997 // If multiple selections contain a given row, avoid processing that
4998 // row more than once.
4999 let mut start_row = selection.start.row;
5000 if last_toggled_row == Some(start_row) {
5001 start_row += 1;
5002 }
5003 let end_row =
5004 if selection.end.row > selection.start.row && selection.end.column == 0 {
5005 selection.end.row - 1
5006 } else {
5007 selection.end.row
5008 };
5009 last_toggled_row = Some(end_row);
5010
5011 if start_row > end_row {
5012 continue;
5013 }
5014
5015 // If the language has line comments, toggle those.
5016 if let Some(full_comment_prefix) = language.line_comment_prefix() {
5017 // Split the comment prefix's trailing whitespace into a separate string,
5018 // as that portion won't be used for detecting if a line is a comment.
5019 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
5020 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
5021 let mut all_selection_lines_are_comments = true;
5022
5023 for row in start_row..=end_row {
5024 if snapshot.is_line_blank(row) {
5025 continue;
5026 }
5027
5028 let prefix_range = comment_prefix_range(
5029 snapshot.deref(),
5030 row,
5031 comment_prefix,
5032 comment_prefix_whitespace,
5033 );
5034 if prefix_range.is_empty() {
5035 all_selection_lines_are_comments = false;
5036 }
5037 selection_edit_ranges.push(prefix_range);
5038 }
5039
5040 if all_selection_lines_are_comments {
5041 edits.extend(
5042 selection_edit_ranges
5043 .iter()
5044 .cloned()
5045 .map(|range| (range, empty_str.clone())),
5046 );
5047 } else {
5048 let min_column = selection_edit_ranges
5049 .iter()
5050 .map(|r| r.start.column)
5051 .min()
5052 .unwrap_or(0);
5053 edits.extend(selection_edit_ranges.iter().map(|range| {
5054 let position = Point::new(range.start.row, min_column);
5055 (position..position, full_comment_prefix.clone())
5056 }));
5057 }
5058 } else if let Some((full_comment_prefix, comment_suffix)) =
5059 language.block_comment_delimiters()
5060 {
5061 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
5062 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
5063 let prefix_range = comment_prefix_range(
5064 snapshot.deref(),
5065 start_row,
5066 comment_prefix,
5067 comment_prefix_whitespace,
5068 );
5069 let suffix_range = comment_suffix_range(
5070 snapshot.deref(),
5071 end_row,
5072 comment_suffix.trim_start_matches(' '),
5073 comment_suffix.starts_with(' '),
5074 );
5075
5076 if prefix_range.is_empty() || suffix_range.is_empty() {
5077 edits.push((
5078 prefix_range.start..prefix_range.start,
5079 full_comment_prefix.clone(),
5080 ));
5081 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
5082 suffixes_inserted.push((end_row, comment_suffix.len()));
5083 } else {
5084 edits.push((prefix_range, empty_str.clone()));
5085 edits.push((suffix_range, empty_str.clone()));
5086 }
5087 } else {
5088 continue;
5089 }
5090 }
5091
5092 drop(snapshot);
5093 this.buffer.update(cx, |buffer, cx| {
5094 buffer.edit(edits, None, cx);
5095 });
5096
5097 // Adjust selections so that they end before any comment suffixes that
5098 // were inserted.
5099 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
5100 let mut selections = this.selections.all::<Point>(cx);
5101 let snapshot = this.buffer.read(cx).read(cx);
5102 for selection in &mut selections {
5103 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
5104 match row.cmp(&selection.end.row) {
5105 Ordering::Less => {
5106 suffixes_inserted.next();
5107 continue;
5108 }
5109 Ordering::Greater => break,
5110 Ordering::Equal => {
5111 if selection.end.column == snapshot.line_len(row) {
5112 if selection.is_empty() {
5113 selection.start.column -= suffix_len as u32;
5114 }
5115 selection.end.column -= suffix_len as u32;
5116 }
5117 break;
5118 }
5119 }
5120 }
5121 }
5122
5123 drop(snapshot);
5124 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5125
5126 let selections = this.selections.all::<Point>(cx);
5127 let selections_on_single_row = selections.windows(2).all(|selections| {
5128 selections[0].start.row == selections[1].start.row
5129 && selections[0].end.row == selections[1].end.row
5130 && selections[0].start.row == selections[0].end.row
5131 });
5132 let selections_selecting = selections
5133 .iter()
5134 .any(|selection| selection.start != selection.end);
5135 let advance_downwards = action.advance_downwards
5136 && selections_on_single_row
5137 && !selections_selecting
5138 && this.mode != EditorMode::SingleLine;
5139
5140 if advance_downwards {
5141 let snapshot = this.buffer.read(cx).snapshot(cx);
5142
5143 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5144 s.move_cursors_with(|display_snapshot, display_point, _| {
5145 let mut point = display_point.to_point(display_snapshot);
5146 point.row += 1;
5147 point = snapshot.clip_point(point, Bias::Left);
5148 let display_point = point.to_display_point(display_snapshot);
5149 (display_point, SelectionGoal::Column(display_point.column()))
5150 })
5151 });
5152 }
5153 });
5154 }
5155
5156 pub fn select_larger_syntax_node(
5157 &mut self,
5158 _: &SelectLargerSyntaxNode,
5159 cx: &mut ViewContext<Self>,
5160 ) {
5161 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5162 let buffer = self.buffer.read(cx).snapshot(cx);
5163 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
5164
5165 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
5166 let mut selected_larger_node = false;
5167 let new_selections = old_selections
5168 .iter()
5169 .map(|selection| {
5170 let old_range = selection.start..selection.end;
5171 let mut new_range = old_range.clone();
5172 while let Some(containing_range) =
5173 buffer.range_for_syntax_ancestor(new_range.clone())
5174 {
5175 new_range = containing_range;
5176 if !display_map.intersects_fold(new_range.start)
5177 && !display_map.intersects_fold(new_range.end)
5178 {
5179 break;
5180 }
5181 }
5182
5183 selected_larger_node |= new_range != old_range;
5184 Selection {
5185 id: selection.id,
5186 start: new_range.start,
5187 end: new_range.end,
5188 goal: SelectionGoal::None,
5189 reversed: selection.reversed,
5190 }
5191 })
5192 .collect::<Vec<_>>();
5193
5194 if selected_larger_node {
5195 stack.push(old_selections);
5196 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5197 s.select(new_selections);
5198 });
5199 }
5200 self.select_larger_syntax_node_stack = stack;
5201 }
5202
5203 pub fn select_smaller_syntax_node(
5204 &mut self,
5205 _: &SelectSmallerSyntaxNode,
5206 cx: &mut ViewContext<Self>,
5207 ) {
5208 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
5209 if let Some(selections) = stack.pop() {
5210 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5211 s.select(selections.to_vec());
5212 });
5213 }
5214 self.select_larger_syntax_node_stack = stack;
5215 }
5216
5217 pub fn move_to_enclosing_bracket(
5218 &mut self,
5219 _: &MoveToEnclosingBracket,
5220 cx: &mut ViewContext<Self>,
5221 ) {
5222 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5223 s.move_offsets_with(|snapshot, selection| {
5224 let Some(enclosing_bracket_ranges) = snapshot.enclosing_bracket_ranges(selection.start..selection.end) else {
5225 return;
5226 };
5227
5228 let mut best_length = usize::MAX;
5229 let mut best_inside = false;
5230 let mut best_in_bracket_range = false;
5231 let mut best_destination = None;
5232 for (open, close) in enclosing_bracket_ranges {
5233 let close = close.to_inclusive();
5234 let length = close.end() - open.start;
5235 let inside = selection.start >= open.end && selection.end <= *close.start();
5236 let in_bracket_range = open.to_inclusive().contains(&selection.head()) || close.contains(&selection.head());
5237
5238 // If best is next to a bracket and current isn't, skip
5239 if !in_bracket_range && best_in_bracket_range {
5240 continue;
5241 }
5242
5243 // Prefer smaller lengths unless best is inside and current isn't
5244 if length > best_length && (best_inside || !inside) {
5245 continue;
5246 }
5247
5248 best_length = length;
5249 best_inside = inside;
5250 best_in_bracket_range = in_bracket_range;
5251 best_destination = Some(if close.contains(&selection.start) && close.contains(&selection.end) {
5252 if inside {
5253 open.end
5254 } else {
5255 open.start
5256 }
5257 } else {
5258 if inside {
5259 *close.start()
5260 } else {
5261 *close.end()
5262 }
5263 });
5264 }
5265
5266 if let Some(destination) = best_destination {
5267 selection.collapse_to(destination, SelectionGoal::None);
5268 }
5269 })
5270 });
5271 }
5272
5273 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
5274 self.end_selection(cx);
5275 self.selection_history.mode = SelectionHistoryMode::Undoing;
5276 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
5277 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
5278 self.select_next_state = entry.select_next_state;
5279 self.add_selections_state = entry.add_selections_state;
5280 self.request_autoscroll(Autoscroll::newest(), cx);
5281 }
5282 self.selection_history.mode = SelectionHistoryMode::Normal;
5283 }
5284
5285 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
5286 self.end_selection(cx);
5287 self.selection_history.mode = SelectionHistoryMode::Redoing;
5288 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
5289 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
5290 self.select_next_state = entry.select_next_state;
5291 self.add_selections_state = entry.add_selections_state;
5292 self.request_autoscroll(Autoscroll::newest(), cx);
5293 }
5294 self.selection_history.mode = SelectionHistoryMode::Normal;
5295 }
5296
5297 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
5298 self.go_to_diagnostic_impl(Direction::Next, cx)
5299 }
5300
5301 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
5302 self.go_to_diagnostic_impl(Direction::Prev, cx)
5303 }
5304
5305 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
5306 let buffer = self.buffer.read(cx).snapshot(cx);
5307 let selection = self.selections.newest::<usize>(cx);
5308
5309 // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
5310 if direction == Direction::Next {
5311 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
5312 let (group_id, jump_to) = popover.activation_info();
5313 if self.activate_diagnostics(group_id, cx) {
5314 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5315 let mut new_selection = s.newest_anchor().clone();
5316 new_selection.collapse_to(jump_to, SelectionGoal::None);
5317 s.select_anchors(vec![new_selection.clone()]);
5318 });
5319 }
5320 return;
5321 }
5322 }
5323
5324 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
5325 active_diagnostics
5326 .primary_range
5327 .to_offset(&buffer)
5328 .to_inclusive()
5329 });
5330 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
5331 if active_primary_range.contains(&selection.head()) {
5332 *active_primary_range.end()
5333 } else {
5334 selection.head()
5335 }
5336 } else {
5337 selection.head()
5338 };
5339
5340 loop {
5341 let mut diagnostics = if direction == Direction::Prev {
5342 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
5343 } else {
5344 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
5345 };
5346 let group = diagnostics.find_map(|entry| {
5347 if entry.diagnostic.is_primary
5348 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
5349 && !entry.range.is_empty()
5350 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
5351 {
5352 Some((entry.range, entry.diagnostic.group_id))
5353 } else {
5354 None
5355 }
5356 });
5357
5358 if let Some((primary_range, group_id)) = group {
5359 if self.activate_diagnostics(group_id, cx) {
5360 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5361 s.select(vec![Selection {
5362 id: selection.id,
5363 start: primary_range.start,
5364 end: primary_range.start,
5365 reversed: false,
5366 goal: SelectionGoal::None,
5367 }]);
5368 });
5369 }
5370 break;
5371 } else {
5372 // Cycle around to the start of the buffer, potentially moving back to the start of
5373 // the currently active diagnostic.
5374 active_primary_range.take();
5375 if direction == Direction::Prev {
5376 if search_start == buffer.len() {
5377 break;
5378 } else {
5379 search_start = buffer.len();
5380 }
5381 } else if search_start == 0 {
5382 break;
5383 } else {
5384 search_start = 0;
5385 }
5386 }
5387 }
5388 }
5389
5390 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
5391 self.go_to_hunk_impl(Direction::Next, cx)
5392 }
5393
5394 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
5395 self.go_to_hunk_impl(Direction::Prev, cx)
5396 }
5397
5398 pub fn go_to_hunk_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
5399 let snapshot = self
5400 .display_map
5401 .update(cx, |display_map, cx| display_map.snapshot(cx));
5402 let selection = self.selections.newest::<Point>(cx);
5403
5404 fn seek_in_direction(
5405 this: &mut Editor,
5406 snapshot: &DisplaySnapshot,
5407 initial_point: Point,
5408 is_wrapped: bool,
5409 direction: Direction,
5410 cx: &mut ViewContext<Editor>,
5411 ) -> bool {
5412 let hunks = if direction == Direction::Next {
5413 snapshot
5414 .buffer_snapshot
5415 .git_diff_hunks_in_range(initial_point.row..u32::MAX, false)
5416 } else {
5417 snapshot
5418 .buffer_snapshot
5419 .git_diff_hunks_in_range(0..initial_point.row, true)
5420 };
5421
5422 let display_point = initial_point.to_display_point(snapshot);
5423 let mut hunks = hunks
5424 .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
5425 .skip_while(|hunk| {
5426 if is_wrapped {
5427 false
5428 } else {
5429 hunk.contains_display_row(display_point.row())
5430 }
5431 })
5432 .dedup();
5433
5434 if let Some(hunk) = hunks.next() {
5435 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5436 let row = hunk.start_display_row();
5437 let point = DisplayPoint::new(row, 0);
5438 s.select_display_ranges([point..point]);
5439 });
5440
5441 true
5442 } else {
5443 false
5444 }
5445 }
5446
5447 if !seek_in_direction(self, &snapshot, selection.head(), false, direction, cx) {
5448 let wrapped_point = match direction {
5449 Direction::Next => Point::zero(),
5450 Direction::Prev => snapshot.buffer_snapshot.max_point(),
5451 };
5452 seek_in_direction(self, &snapshot, wrapped_point, true, direction, cx);
5453 }
5454 }
5455
5456 pub fn go_to_definition(
5457 workspace: &mut Workspace,
5458 _: &GoToDefinition,
5459 cx: &mut ViewContext<Workspace>,
5460 ) {
5461 Self::go_to_definition_of_kind(GotoDefinitionKind::Symbol, workspace, cx);
5462 }
5463
5464 pub fn go_to_type_definition(
5465 workspace: &mut Workspace,
5466 _: &GoToTypeDefinition,
5467 cx: &mut ViewContext<Workspace>,
5468 ) {
5469 Self::go_to_definition_of_kind(GotoDefinitionKind::Type, workspace, cx);
5470 }
5471
5472 fn go_to_definition_of_kind(
5473 kind: GotoDefinitionKind,
5474 workspace: &mut Workspace,
5475 cx: &mut ViewContext<Workspace>,
5476 ) {
5477 let active_item = workspace.active_item(cx);
5478 let editor_handle = if let Some(editor) = active_item
5479 .as_ref()
5480 .and_then(|item| item.act_as::<Self>(cx))
5481 {
5482 editor
5483 } else {
5484 return;
5485 };
5486
5487 let editor = editor_handle.read(cx);
5488 let buffer = editor.buffer.read(cx);
5489 let head = editor.selections.newest::<usize>(cx).head();
5490 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
5491 text_anchor
5492 } else {
5493 return;
5494 };
5495
5496 let project = workspace.project().clone();
5497 let definitions = project.update(cx, |project, cx| match kind {
5498 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
5499 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
5500 });
5501
5502 cx.spawn_labeled("Fetching Definition...", |workspace, mut cx| async move {
5503 let definitions = definitions.await?;
5504 workspace.update(&mut cx, |workspace, cx| {
5505 Editor::navigate_to_definitions(workspace, editor_handle, definitions, cx);
5506 });
5507
5508 Ok::<(), anyhow::Error>(())
5509 })
5510 .detach_and_log_err(cx);
5511 }
5512
5513 pub fn navigate_to_definitions(
5514 workspace: &mut Workspace,
5515 editor_handle: ViewHandle<Editor>,
5516 definitions: Vec<LocationLink>,
5517 cx: &mut ViewContext<Workspace>,
5518 ) {
5519 let pane = workspace.active_pane().clone();
5520 // If there is one definition, just open it directly
5521 if let [definition] = definitions.as_slice() {
5522 let range = definition
5523 .target
5524 .range
5525 .to_offset(definition.target.buffer.read(cx));
5526
5527 let target_editor_handle =
5528 workspace.open_project_item(definition.target.buffer.clone(), cx);
5529 target_editor_handle.update(cx, |target_editor, cx| {
5530 // When selecting a definition in a different buffer, disable the nav history
5531 // to avoid creating a history entry at the previous cursor location.
5532 if editor_handle != target_editor_handle {
5533 pane.update(cx, |pane, _| pane.disable_history());
5534 }
5535 target_editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
5536 s.select_ranges([range]);
5537 });
5538
5539 pane.update(cx, |pane, _| pane.enable_history());
5540 });
5541 } else if !definitions.is_empty() {
5542 let replica_id = editor_handle.read(cx).replica_id(cx);
5543 let title = definitions
5544 .iter()
5545 .find(|definition| definition.origin.is_some())
5546 .and_then(|definition| {
5547 definition.origin.as_ref().map(|origin| {
5548 let buffer = origin.buffer.read(cx);
5549 format!(
5550 "Definitions for {}",
5551 buffer
5552 .text_for_range(origin.range.clone())
5553 .collect::<String>()
5554 )
5555 })
5556 })
5557 .unwrap_or("Definitions".to_owned());
5558 let locations = definitions
5559 .into_iter()
5560 .map(|definition| definition.target)
5561 .collect();
5562 Self::open_locations_in_multibuffer(workspace, locations, replica_id, title, cx)
5563 }
5564 }
5565
5566 pub fn find_all_references(
5567 workspace: &mut Workspace,
5568 _: &FindAllReferences,
5569 cx: &mut ViewContext<Workspace>,
5570 ) -> Option<Task<Result<()>>> {
5571 let active_item = workspace.active_item(cx)?;
5572 let editor_handle = active_item.act_as::<Self>(cx)?;
5573
5574 let editor = editor_handle.read(cx);
5575 let buffer = editor.buffer.read(cx);
5576 let head = editor.selections.newest::<usize>(cx).head();
5577 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
5578 let replica_id = editor.replica_id(cx);
5579
5580 let project = workspace.project().clone();
5581 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
5582 Some(cx.spawn_labeled(
5583 "Finding All References...",
5584 |workspace, mut cx| async move {
5585 let locations = references.await?;
5586 if locations.is_empty() {
5587 return Ok(());
5588 }
5589
5590 workspace.update(&mut cx, |workspace, cx| {
5591 let title = locations
5592 .first()
5593 .as_ref()
5594 .map(|location| {
5595 let buffer = location.buffer.read(cx);
5596 format!(
5597 "References to `{}`",
5598 buffer
5599 .text_for_range(location.range.clone())
5600 .collect::<String>()
5601 )
5602 })
5603 .unwrap();
5604 Self::open_locations_in_multibuffer(
5605 workspace, locations, replica_id, title, cx,
5606 );
5607 });
5608
5609 Ok(())
5610 },
5611 ))
5612 }
5613
5614 /// Opens a multibuffer with the given project locations in it
5615 pub fn open_locations_in_multibuffer(
5616 workspace: &mut Workspace,
5617 mut locations: Vec<Location>,
5618 replica_id: ReplicaId,
5619 title: String,
5620 cx: &mut ViewContext<Workspace>,
5621 ) {
5622 // If there are multiple definitions, open them in a multibuffer
5623 locations.sort_by_key(|location| location.buffer.id());
5624 let mut locations = locations.into_iter().peekable();
5625 let mut ranges_to_highlight = Vec::new();
5626
5627 let excerpt_buffer = cx.add_model(|cx| {
5628 let mut multibuffer = MultiBuffer::new(replica_id);
5629 while let Some(location) = locations.next() {
5630 let buffer = location.buffer.read(cx);
5631 let mut ranges_for_buffer = Vec::new();
5632 let range = location.range.to_offset(buffer);
5633 ranges_for_buffer.push(range.clone());
5634
5635 while let Some(next_location) = locations.peek() {
5636 if next_location.buffer == location.buffer {
5637 ranges_for_buffer.push(next_location.range.to_offset(buffer));
5638 locations.next();
5639 } else {
5640 break;
5641 }
5642 }
5643
5644 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
5645 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
5646 location.buffer.clone(),
5647 ranges_for_buffer,
5648 1,
5649 cx,
5650 ))
5651 }
5652
5653 multibuffer.with_title(title)
5654 });
5655
5656 let editor = cx.add_view(|cx| {
5657 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
5658 });
5659 editor.update(cx, |editor, cx| {
5660 editor.highlight_background::<Self>(
5661 ranges_to_highlight,
5662 |theme| theme.editor.highlighted_line_background,
5663 cx,
5664 );
5665 });
5666 workspace.add_item(Box::new(editor), cx);
5667 }
5668
5669 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
5670 use language::ToOffset as _;
5671
5672 let project = self.project.clone()?;
5673 let selection = self.selections.newest_anchor().clone();
5674 let (cursor_buffer, cursor_buffer_position) = self
5675 .buffer
5676 .read(cx)
5677 .text_anchor_for_position(selection.head(), cx)?;
5678 let (tail_buffer, _) = self
5679 .buffer
5680 .read(cx)
5681 .text_anchor_for_position(selection.tail(), cx)?;
5682 if tail_buffer != cursor_buffer {
5683 return None;
5684 }
5685
5686 let snapshot = cursor_buffer.read(cx).snapshot();
5687 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
5688 let prepare_rename = project.update(cx, |project, cx| {
5689 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
5690 });
5691
5692 Some(cx.spawn(|this, mut cx| async move {
5693 let rename_range = if let Some(range) = prepare_rename.await? {
5694 Some(range)
5695 } else {
5696 this.read_with(&cx, |this, cx| {
5697 let buffer = this.buffer.read(cx).snapshot(cx);
5698 let mut buffer_highlights = this
5699 .document_highlights_for_position(selection.head(), &buffer)
5700 .filter(|highlight| {
5701 highlight.start.excerpt_id() == selection.head().excerpt_id()
5702 && highlight.end.excerpt_id() == selection.head().excerpt_id()
5703 });
5704 buffer_highlights
5705 .next()
5706 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
5707 })
5708 };
5709 if let Some(rename_range) = rename_range {
5710 let rename_buffer_range = rename_range.to_offset(&snapshot);
5711 let cursor_offset_in_rename_range =
5712 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
5713
5714 this.update(&mut cx, |this, cx| {
5715 this.take_rename(false, cx);
5716 let style = this.style(cx);
5717 let buffer = this.buffer.read(cx).read(cx);
5718 let cursor_offset = selection.head().to_offset(&buffer);
5719 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
5720 let rename_end = rename_start + rename_buffer_range.len();
5721 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
5722 let mut old_highlight_id = None;
5723 let old_name: Arc<str> = buffer
5724 .chunks(rename_start..rename_end, true)
5725 .map(|chunk| {
5726 if old_highlight_id.is_none() {
5727 old_highlight_id = chunk.syntax_highlight_id;
5728 }
5729 chunk.text
5730 })
5731 .collect::<String>()
5732 .into();
5733
5734 drop(buffer);
5735
5736 // Position the selection in the rename editor so that it matches the current selection.
5737 this.show_local_selections = false;
5738 let rename_editor = cx.add_view(|cx| {
5739 let mut editor = Editor::single_line(None, cx);
5740 if let Some(old_highlight_id) = old_highlight_id {
5741 editor.override_text_style =
5742 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
5743 }
5744 editor.buffer.update(cx, |buffer, cx| {
5745 buffer.edit([(0..0, old_name.clone())], None, cx)
5746 });
5747 editor.select_all(&SelectAll, cx);
5748 editor
5749 });
5750
5751 let ranges = this
5752 .clear_background_highlights::<DocumentHighlightWrite>(cx)
5753 .into_iter()
5754 .flat_map(|(_, ranges)| ranges)
5755 .chain(
5756 this.clear_background_highlights::<DocumentHighlightRead>(cx)
5757 .into_iter()
5758 .flat_map(|(_, ranges)| ranges),
5759 )
5760 .collect();
5761
5762 this.highlight_text::<Rename>(
5763 ranges,
5764 HighlightStyle {
5765 fade_out: Some(style.rename_fade),
5766 ..Default::default()
5767 },
5768 cx,
5769 );
5770 cx.focus(&rename_editor);
5771 let block_id = this.insert_blocks(
5772 [BlockProperties {
5773 style: BlockStyle::Flex,
5774 position: range.start.clone(),
5775 height: 1,
5776 render: Arc::new({
5777 let editor = rename_editor.clone();
5778 move |cx: &mut BlockContext| {
5779 ChildView::new(&editor, cx)
5780 .contained()
5781 .with_padding_left(cx.anchor_x)
5782 .boxed()
5783 }
5784 }),
5785 disposition: BlockDisposition::Below,
5786 }],
5787 cx,
5788 )[0];
5789 this.pending_rename = Some(RenameState {
5790 range,
5791 old_name,
5792 editor: rename_editor,
5793 block_id,
5794 });
5795 });
5796 }
5797
5798 Ok(())
5799 }))
5800 }
5801
5802 pub fn confirm_rename(
5803 workspace: &mut Workspace,
5804 _: &ConfirmRename,
5805 cx: &mut ViewContext<Workspace>,
5806 ) -> Option<Task<Result<()>>> {
5807 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
5808
5809 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
5810 let rename = editor.take_rename(false, cx)?;
5811 let buffer = editor.buffer.read(cx);
5812 let (start_buffer, start) =
5813 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
5814 let (end_buffer, end) =
5815 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
5816 if start_buffer == end_buffer {
5817 let new_name = rename.editor.read(cx).text(cx);
5818 Some((start_buffer, start..end, rename.old_name, new_name))
5819 } else {
5820 None
5821 }
5822 })?;
5823
5824 let rename = workspace.project().clone().update(cx, |project, cx| {
5825 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
5826 });
5827
5828 Some(cx.spawn(|workspace, mut cx| async move {
5829 let project_transaction = rename.await?;
5830 Self::open_project_transaction(
5831 editor.clone(),
5832 workspace,
5833 project_transaction,
5834 format!("Rename: {} → {}", old_name, new_name),
5835 cx.clone(),
5836 )
5837 .await?;
5838
5839 editor.update(&mut cx, |editor, cx| {
5840 editor.refresh_document_highlights(cx);
5841 });
5842 Ok(())
5843 }))
5844 }
5845
5846 fn take_rename(
5847 &mut self,
5848 moving_cursor: bool,
5849 cx: &mut ViewContext<Self>,
5850 ) -> Option<RenameState> {
5851 let rename = self.pending_rename.take()?;
5852 self.remove_blocks([rename.block_id].into_iter().collect(), cx);
5853 self.clear_text_highlights::<Rename>(cx);
5854 self.show_local_selections = true;
5855
5856 if moving_cursor {
5857 let rename_editor = rename.editor.read(cx);
5858 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
5859
5860 // Update the selection to match the position of the selection inside
5861 // the rename editor.
5862 let snapshot = self.buffer.read(cx).read(cx);
5863 let rename_range = rename.range.to_offset(&snapshot);
5864 let cursor_in_editor = snapshot
5865 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
5866 .min(rename_range.end);
5867 drop(snapshot);
5868
5869 self.change_selections(None, cx, |s| {
5870 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
5871 });
5872 } else {
5873 self.refresh_document_highlights(cx);
5874 }
5875
5876 Some(rename)
5877 }
5878
5879 #[cfg(any(test, feature = "test-support"))]
5880 pub fn pending_rename(&self) -> Option<&RenameState> {
5881 self.pending_rename.as_ref()
5882 }
5883
5884 fn format(&mut self, _: &Format, cx: &mut ViewContext<'_, Self>) -> Option<Task<Result<()>>> {
5885 let project = match &self.project {
5886 Some(project) => project.clone(),
5887 None => return None,
5888 };
5889
5890 Some(self.perform_format(project, FormatTrigger::Manual, cx))
5891 }
5892
5893 fn perform_format(
5894 &mut self,
5895 project: ModelHandle<Project>,
5896 trigger: FormatTrigger,
5897 cx: &mut ViewContext<'_, Self>,
5898 ) -> Task<Result<()>> {
5899 let buffer = self.buffer().clone();
5900 let buffers = buffer.read(cx).all_buffers();
5901
5902 let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
5903 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
5904
5905 cx.spawn(|_, mut cx| async move {
5906 let transaction = futures::select_biased! {
5907 _ = timeout => {
5908 log::warn!("timed out waiting for formatting");
5909 None
5910 }
5911 transaction = format.log_err().fuse() => transaction,
5912 };
5913
5914 buffer.update(&mut cx, |buffer, cx| {
5915 if let Some(transaction) = transaction {
5916 if !buffer.is_singleton() {
5917 buffer.push_transaction(&transaction.0);
5918 }
5919 }
5920
5921 cx.notify();
5922 });
5923
5924 Ok(())
5925 })
5926 }
5927
5928 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
5929 if let Some(project) = self.project.clone() {
5930 self.buffer.update(cx, |multi_buffer, cx| {
5931 project.update(cx, |project, cx| {
5932 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
5933 });
5934 })
5935 }
5936 }
5937
5938 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
5939 cx.show_character_palette();
5940 }
5941
5942 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
5943 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
5944 let buffer = self.buffer.read(cx).snapshot(cx);
5945 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
5946 let is_valid = buffer
5947 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
5948 .any(|entry| {
5949 entry.diagnostic.is_primary
5950 && !entry.range.is_empty()
5951 && entry.range.start == primary_range_start
5952 && entry.diagnostic.message == active_diagnostics.primary_message
5953 });
5954
5955 if is_valid != active_diagnostics.is_valid {
5956 active_diagnostics.is_valid = is_valid;
5957 let mut new_styles = HashMap::default();
5958 for (block_id, diagnostic) in &active_diagnostics.blocks {
5959 new_styles.insert(
5960 *block_id,
5961 diagnostic_block_renderer(diagnostic.clone(), is_valid),
5962 );
5963 }
5964 self.display_map
5965 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
5966 }
5967 }
5968 }
5969
5970 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
5971 self.dismiss_diagnostics(cx);
5972 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
5973 let buffer = self.buffer.read(cx).snapshot(cx);
5974
5975 let mut primary_range = None;
5976 let mut primary_message = None;
5977 let mut group_end = Point::zero();
5978 let diagnostic_group = buffer
5979 .diagnostic_group::<Point>(group_id)
5980 .map(|entry| {
5981 if entry.range.end > group_end {
5982 group_end = entry.range.end;
5983 }
5984 if entry.diagnostic.is_primary {
5985 primary_range = Some(entry.range.clone());
5986 primary_message = Some(entry.diagnostic.message.clone());
5987 }
5988 entry
5989 })
5990 .collect::<Vec<_>>();
5991 let primary_range = primary_range?;
5992 let primary_message = primary_message?;
5993 let primary_range =
5994 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
5995
5996 let blocks = display_map
5997 .insert_blocks(
5998 diagnostic_group.iter().map(|entry| {
5999 let diagnostic = entry.diagnostic.clone();
6000 let message_height = diagnostic.message.lines().count() as u8;
6001 BlockProperties {
6002 style: BlockStyle::Fixed,
6003 position: buffer.anchor_after(entry.range.start),
6004 height: message_height,
6005 render: diagnostic_block_renderer(diagnostic, true),
6006 disposition: BlockDisposition::Below,
6007 }
6008 }),
6009 cx,
6010 )
6011 .into_iter()
6012 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
6013 .collect();
6014
6015 Some(ActiveDiagnosticGroup {
6016 primary_range,
6017 primary_message,
6018 blocks,
6019 is_valid: true,
6020 })
6021 });
6022 self.active_diagnostics.is_some()
6023 }
6024
6025 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
6026 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
6027 self.display_map.update(cx, |display_map, cx| {
6028 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
6029 });
6030 cx.notify();
6031 }
6032 }
6033
6034 pub fn set_selections_from_remote(
6035 &mut self,
6036 selections: Vec<Selection<Anchor>>,
6037 pending_selection: Option<Selection<Anchor>>,
6038 cx: &mut ViewContext<Self>,
6039 ) {
6040 let old_cursor_position = self.selections.newest_anchor().head();
6041 self.selections.change_with(cx, |s| {
6042 s.select_anchors(selections);
6043 if let Some(pending_selection) = pending_selection {
6044 s.set_pending(pending_selection, SelectMode::Character);
6045 } else {
6046 s.clear_pending();
6047 }
6048 });
6049 self.selections_did_change(false, &old_cursor_position, cx);
6050 }
6051
6052 fn push_to_selection_history(&mut self) {
6053 self.selection_history.push(SelectionHistoryEntry {
6054 selections: self.selections.disjoint_anchors(),
6055 select_next_state: self.select_next_state.clone(),
6056 add_selections_state: self.add_selections_state.clone(),
6057 });
6058 }
6059
6060 pub fn transact(
6061 &mut self,
6062 cx: &mut ViewContext<Self>,
6063 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
6064 ) -> Option<TransactionId> {
6065 self.start_transaction_at(Instant::now(), cx);
6066 update(self, cx);
6067 self.end_transaction_at(Instant::now(), cx)
6068 }
6069
6070 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
6071 self.end_selection(cx);
6072 if let Some(tx_id) = self
6073 .buffer
6074 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
6075 {
6076 self.selection_history
6077 .insert_transaction(tx_id, self.selections.disjoint_anchors());
6078 }
6079 }
6080
6081 fn end_transaction_at(
6082 &mut self,
6083 now: Instant,
6084 cx: &mut ViewContext<Self>,
6085 ) -> Option<TransactionId> {
6086 if let Some(tx_id) = self
6087 .buffer
6088 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
6089 {
6090 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
6091 *end_selections = Some(self.selections.disjoint_anchors());
6092 } else {
6093 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
6094 }
6095
6096 cx.emit(Event::Edited);
6097 Some(tx_id)
6098 } else {
6099 None
6100 }
6101 }
6102
6103 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
6104 let mut fold_ranges = Vec::new();
6105
6106 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6107
6108 let selections = self.selections.all::<Point>(cx);
6109 for selection in selections {
6110 let range = selection.range().sorted();
6111 let buffer_start_row = range.start.row;
6112
6113 for row in (0..=range.end.row).rev() {
6114 let fold_range = display_map.foldable_range(row);
6115
6116 if let Some(fold_range) = fold_range {
6117 if fold_range.end.row >= buffer_start_row {
6118 fold_ranges.push(fold_range);
6119 if row <= range.start.row {
6120 break;
6121 }
6122 }
6123 }
6124 }
6125 }
6126
6127 self.fold_ranges(fold_ranges, true, cx);
6128 }
6129
6130 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
6131 let buffer_row = fold_at.buffer_row;
6132 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6133
6134 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
6135 let autoscroll = self
6136 .selections
6137 .all::<Point>(cx)
6138 .iter()
6139 .any(|selection| fold_range.overlaps(&selection.range()));
6140
6141 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
6142 }
6143 }
6144
6145 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
6146 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6147 let buffer = &display_map.buffer_snapshot;
6148 let selections = self.selections.all::<Point>(cx);
6149 let ranges = selections
6150 .iter()
6151 .map(|s| {
6152 let range = s.display_range(&display_map).sorted();
6153 let mut start = range.start.to_point(&display_map);
6154 let mut end = range.end.to_point(&display_map);
6155 start.column = 0;
6156 end.column = buffer.line_len(end.row);
6157 start..end
6158 })
6159 .collect::<Vec<_>>();
6160
6161 self.unfold_ranges(ranges, true, true, cx);
6162 }
6163
6164 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
6165 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6166
6167 let intersection_range = Point::new(unfold_at.buffer_row, 0)
6168 ..Point::new(
6169 unfold_at.buffer_row,
6170 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
6171 );
6172
6173 let autoscroll = self
6174 .selections
6175 .all::<Point>(cx)
6176 .iter()
6177 .any(|selection| selection.range().overlaps(&intersection_range));
6178
6179 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
6180 }
6181
6182 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
6183 let selections = self.selections.all::<Point>(cx);
6184 let ranges = selections.into_iter().map(|s| s.start..s.end);
6185 self.fold_ranges(ranges, true, cx);
6186 }
6187
6188 pub fn fold_ranges<T: ToOffset + Clone>(
6189 &mut self,
6190 ranges: impl IntoIterator<Item = Range<T>>,
6191 auto_scroll: bool,
6192 cx: &mut ViewContext<Self>,
6193 ) {
6194 let mut ranges = ranges.into_iter().peekable();
6195 if ranges.peek().is_some() {
6196 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
6197
6198 if auto_scroll {
6199 self.request_autoscroll(Autoscroll::fit(), cx);
6200 }
6201
6202 cx.notify();
6203 }
6204 }
6205
6206 pub fn unfold_ranges<T: ToOffset + Clone>(
6207 &mut self,
6208 ranges: impl IntoIterator<Item = Range<T>>,
6209 inclusive: bool,
6210 auto_scroll: bool,
6211 cx: &mut ViewContext<Self>,
6212 ) {
6213 let mut ranges = ranges.into_iter().peekable();
6214 if ranges.peek().is_some() {
6215 self.display_map
6216 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
6217 if auto_scroll {
6218 self.request_autoscroll(Autoscroll::fit(), cx);
6219 }
6220
6221 cx.notify();
6222 }
6223 }
6224
6225 pub fn gutter_hover(
6226 &mut self,
6227 GutterHover { hovered }: &GutterHover,
6228 cx: &mut ViewContext<Self>,
6229 ) {
6230 self.gutter_hovered = *hovered;
6231 cx.notify();
6232 }
6233
6234 pub fn insert_blocks(
6235 &mut self,
6236 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
6237 cx: &mut ViewContext<Self>,
6238 ) -> Vec<BlockId> {
6239 let blocks = self
6240 .display_map
6241 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
6242 self.request_autoscroll(Autoscroll::fit(), cx);
6243 blocks
6244 }
6245
6246 pub fn replace_blocks(
6247 &mut self,
6248 blocks: HashMap<BlockId, RenderBlock>,
6249 cx: &mut ViewContext<Self>,
6250 ) {
6251 self.display_map
6252 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
6253 self.request_autoscroll(Autoscroll::fit(), cx);
6254 }
6255
6256 pub fn remove_blocks(&mut self, block_ids: HashSet<BlockId>, cx: &mut ViewContext<Self>) {
6257 self.display_map.update(cx, |display_map, cx| {
6258 display_map.remove_blocks(block_ids, cx)
6259 });
6260 }
6261
6262 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
6263 self.display_map
6264 .update(cx, |map, cx| map.snapshot(cx))
6265 .longest_row()
6266 }
6267
6268 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
6269 self.display_map
6270 .update(cx, |map, cx| map.snapshot(cx))
6271 .max_point()
6272 }
6273
6274 pub fn text(&self, cx: &AppContext) -> String {
6275 self.buffer.read(cx).read(cx).text()
6276 }
6277
6278 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
6279 self.transact(cx, |this, cx| {
6280 this.buffer
6281 .read(cx)
6282 .as_singleton()
6283 .expect("you can only call set_text on editors for singleton buffers")
6284 .update(cx, |buffer, cx| buffer.set_text(text, cx));
6285 });
6286 }
6287
6288 pub fn display_text(&self, cx: &mut AppContext) -> String {
6289 self.display_map
6290 .update(cx, |map, cx| map.snapshot(cx))
6291 .text()
6292 }
6293
6294 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
6295 let language_name = self
6296 .buffer
6297 .read(cx)
6298 .as_singleton()
6299 .and_then(|singleton_buffer| singleton_buffer.read(cx).language())
6300 .map(|l| l.name());
6301
6302 let settings = cx.global::<Settings>();
6303 let mode = self
6304 .soft_wrap_mode_override
6305 .unwrap_or_else(|| settings.soft_wrap(language_name.as_deref()));
6306 match mode {
6307 settings::SoftWrap::None => SoftWrap::None,
6308 settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
6309 settings::SoftWrap::PreferredLineLength => {
6310 SoftWrap::Column(settings.preferred_line_length(language_name.as_deref()))
6311 }
6312 }
6313 }
6314
6315 pub fn set_soft_wrap_mode(&mut self, mode: settings::SoftWrap, cx: &mut ViewContext<Self>) {
6316 self.soft_wrap_mode_override = Some(mode);
6317 cx.notify();
6318 }
6319
6320 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut AppContext) -> bool {
6321 self.display_map
6322 .update(cx, |map, cx| map.set_wrap_width(width, cx))
6323 }
6324
6325 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
6326 if self.soft_wrap_mode_override.is_some() {
6327 self.soft_wrap_mode_override.take();
6328 } else {
6329 let soft_wrap = match self.soft_wrap_mode(cx) {
6330 SoftWrap::None => settings::SoftWrap::EditorWidth,
6331 SoftWrap::EditorWidth | SoftWrap::Column(_) => settings::SoftWrap::None,
6332 };
6333 self.soft_wrap_mode_override = Some(soft_wrap);
6334 }
6335 cx.notify();
6336 }
6337
6338 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
6339 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6340 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6341 cx.reveal_path(&file.abs_path(cx));
6342 }
6343 }
6344 }
6345
6346 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
6347 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6348 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6349 if let Some(path) = file.abs_path(cx).to_str() {
6350 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
6351 }
6352 }
6353 }
6354 }
6355
6356 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
6357 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6358 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6359 if let Some(path) = file.path().to_str() {
6360 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
6361 }
6362 }
6363 }
6364 }
6365
6366 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
6367 self.highlighted_rows = rows;
6368 }
6369
6370 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
6371 self.highlighted_rows.clone()
6372 }
6373
6374 pub fn highlight_background<T: 'static>(
6375 &mut self,
6376 ranges: Vec<Range<Anchor>>,
6377 color_fetcher: fn(&Theme) -> Color,
6378 cx: &mut ViewContext<Self>,
6379 ) {
6380 self.background_highlights
6381 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
6382 cx.notify();
6383 }
6384
6385 #[allow(clippy::type_complexity)]
6386 pub fn clear_background_highlights<T: 'static>(
6387 &mut self,
6388 cx: &mut ViewContext<Self>,
6389 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
6390 let highlights = self.background_highlights.remove(&TypeId::of::<T>());
6391 if highlights.is_some() {
6392 cx.notify();
6393 }
6394 highlights
6395 }
6396
6397 #[cfg(feature = "test-support")]
6398 pub fn all_background_highlights(
6399 &mut self,
6400 cx: &mut ViewContext<Self>,
6401 ) -> Vec<(Range<DisplayPoint>, Color)> {
6402 let snapshot = self.snapshot(cx);
6403 let buffer = &snapshot.buffer_snapshot;
6404 let start = buffer.anchor_before(0);
6405 let end = buffer.anchor_after(buffer.len());
6406 let theme = cx.global::<Settings>().theme.as_ref();
6407 self.background_highlights_in_range(start..end, &snapshot, theme)
6408 }
6409
6410 fn document_highlights_for_position<'a>(
6411 &'a self,
6412 position: Anchor,
6413 buffer: &'a MultiBufferSnapshot,
6414 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
6415 let read_highlights = self
6416 .background_highlights
6417 .get(&TypeId::of::<DocumentHighlightRead>())
6418 .map(|h| &h.1);
6419 let write_highlights = self
6420 .background_highlights
6421 .get(&TypeId::of::<DocumentHighlightWrite>())
6422 .map(|h| &h.1);
6423 let left_position = position.bias_left(buffer);
6424 let right_position = position.bias_right(buffer);
6425 read_highlights
6426 .into_iter()
6427 .chain(write_highlights)
6428 .flat_map(move |ranges| {
6429 let start_ix = match ranges.binary_search_by(|probe| {
6430 let cmp = probe.end.cmp(&left_position, buffer);
6431 if cmp.is_ge() {
6432 Ordering::Greater
6433 } else {
6434 Ordering::Less
6435 }
6436 }) {
6437 Ok(i) | Err(i) => i,
6438 };
6439
6440 let right_position = right_position.clone();
6441 ranges[start_ix..]
6442 .iter()
6443 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
6444 })
6445 }
6446
6447 pub fn background_highlights_in_range(
6448 &self,
6449 search_range: Range<Anchor>,
6450 display_snapshot: &DisplaySnapshot,
6451 theme: &Theme,
6452 ) -> Vec<(Range<DisplayPoint>, Color)> {
6453 let mut results = Vec::new();
6454 let buffer = &display_snapshot.buffer_snapshot;
6455 for (color_fetcher, ranges) in self.background_highlights.values() {
6456 let color = color_fetcher(theme);
6457 let start_ix = match ranges.binary_search_by(|probe| {
6458 let cmp = probe.end.cmp(&search_range.start, buffer);
6459 if cmp.is_gt() {
6460 Ordering::Greater
6461 } else {
6462 Ordering::Less
6463 }
6464 }) {
6465 Ok(i) | Err(i) => i,
6466 };
6467 for range in &ranges[start_ix..] {
6468 if range.start.cmp(&search_range.end, buffer).is_ge() {
6469 break;
6470 }
6471 let start = range
6472 .start
6473 .to_point(buffer)
6474 .to_display_point(display_snapshot);
6475 let end = range
6476 .end
6477 .to_point(buffer)
6478 .to_display_point(display_snapshot);
6479 results.push((start..end, color))
6480 }
6481 }
6482 results
6483 }
6484
6485 pub fn highlight_text<T: 'static>(
6486 &mut self,
6487 ranges: Vec<Range<Anchor>>,
6488 style: HighlightStyle,
6489 cx: &mut ViewContext<Self>,
6490 ) {
6491 self.display_map.update(cx, |map, _| {
6492 map.highlight_text(TypeId::of::<T>(), ranges, style)
6493 });
6494 cx.notify();
6495 }
6496
6497 pub fn text_highlights<'a, T: 'static>(
6498 &'a self,
6499 cx: &'a AppContext,
6500 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
6501 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
6502 }
6503
6504 pub fn clear_text_highlights<T: 'static>(
6505 &mut self,
6506 cx: &mut ViewContext<Self>,
6507 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
6508 let highlights = self
6509 .display_map
6510 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()));
6511 if highlights.is_some() {
6512 cx.notify();
6513 }
6514 highlights
6515 }
6516
6517 pub fn show_local_cursors(&self, cx: &AppContext) -> bool {
6518 self.blink_manager.read(cx).visible() && self.focused
6519 }
6520
6521 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
6522 cx.notify();
6523 }
6524
6525 fn on_buffer_event(
6526 &mut self,
6527 _: ModelHandle<MultiBuffer>,
6528 event: &multi_buffer::Event,
6529 cx: &mut ViewContext<Self>,
6530 ) {
6531 match event {
6532 multi_buffer::Event::Edited => {
6533 self.refresh_active_diagnostics(cx);
6534 self.refresh_code_actions(cx);
6535 if self.has_active_copilot_suggestion(cx) {
6536 self.update_visible_copilot_suggestion(cx);
6537 }
6538 cx.emit(Event::BufferEdited);
6539 }
6540 multi_buffer::Event::ExcerptsAdded {
6541 buffer,
6542 predecessor,
6543 excerpts,
6544 } => cx.emit(Event::ExcerptsAdded {
6545 buffer: buffer.clone(),
6546 predecessor: *predecessor,
6547 excerpts: excerpts.clone(),
6548 }),
6549 multi_buffer::Event::ExcerptsRemoved { ids } => {
6550 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
6551 }
6552 multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed),
6553 multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
6554 multi_buffer::Event::Saved => cx.emit(Event::Saved),
6555 multi_buffer::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
6556 multi_buffer::Event::Reloaded => cx.emit(Event::TitleChanged),
6557 multi_buffer::Event::Closed => cx.emit(Event::Closed),
6558 multi_buffer::Event::DiagnosticsUpdated => {
6559 self.refresh_active_diagnostics(cx);
6560 }
6561 }
6562 }
6563
6564 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
6565 cx.notify();
6566 }
6567
6568 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
6569 self.refresh_copilot_suggestions(cx);
6570 }
6571
6572 pub fn set_searchable(&mut self, searchable: bool) {
6573 self.searchable = searchable;
6574 }
6575
6576 pub fn searchable(&self) -> bool {
6577 self.searchable
6578 }
6579
6580 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
6581 let active_item = workspace.active_item(cx);
6582 let editor_handle = if let Some(editor) = active_item
6583 .as_ref()
6584 .and_then(|item| item.act_as::<Self>(cx))
6585 {
6586 editor
6587 } else {
6588 cx.propagate_action();
6589 return;
6590 };
6591
6592 let editor = editor_handle.read(cx);
6593 let buffer = editor.buffer.read(cx);
6594 if buffer.is_singleton() {
6595 cx.propagate_action();
6596 return;
6597 }
6598
6599 let mut new_selections_by_buffer = HashMap::default();
6600 for selection in editor.selections.all::<usize>(cx) {
6601 for (buffer, mut range) in
6602 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
6603 {
6604 if selection.reversed {
6605 mem::swap(&mut range.start, &mut range.end);
6606 }
6607 new_selections_by_buffer
6608 .entry(buffer)
6609 .or_insert(Vec::new())
6610 .push(range)
6611 }
6612 }
6613
6614 editor_handle.update(cx, |editor, cx| {
6615 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
6616 });
6617 let pane = workspace.active_pane().clone();
6618 pane.update(cx, |pane, _| pane.disable_history());
6619
6620 // We defer the pane interaction because we ourselves are a workspace item
6621 // and activating a new item causes the pane to call a method on us reentrantly,
6622 // which panics if we're on the stack.
6623 cx.defer(move |workspace, cx| {
6624 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
6625 let editor = workspace.open_project_item::<Self>(buffer, cx);
6626 editor.update(cx, |editor, cx| {
6627 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
6628 s.select_ranges(ranges);
6629 });
6630 });
6631 }
6632
6633 pane.update(cx, |pane, _| pane.enable_history());
6634 });
6635 }
6636
6637 fn jump(workspace: &mut Workspace, action: &Jump, cx: &mut ViewContext<Workspace>) {
6638 let editor = workspace.open_path(action.path.clone(), None, true, cx);
6639 let position = action.position;
6640 let anchor = action.anchor;
6641 cx.spawn_weak(|_, mut cx| async move {
6642 let editor = editor.await.log_err()?.downcast::<Editor>()?;
6643 editor.update(&mut cx, |editor, cx| {
6644 let buffer = editor.buffer().read(cx).as_singleton()?;
6645 let buffer = buffer.read(cx);
6646 let cursor = if buffer.can_resolve(&anchor) {
6647 language::ToPoint::to_point(&anchor, buffer)
6648 } else {
6649 buffer.clip_point(position, Bias::Left)
6650 };
6651
6652 let nav_history = editor.nav_history.take();
6653 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
6654 s.select_ranges([cursor..cursor]);
6655 });
6656 editor.nav_history = nav_history;
6657
6658 Some(())
6659 })?;
6660 Some(())
6661 })
6662 .detach()
6663 }
6664
6665 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
6666 let snapshot = self.buffer.read(cx).read(cx);
6667 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
6668 Some(
6669 ranges
6670 .iter()
6671 .map(move |range| {
6672 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
6673 })
6674 .collect(),
6675 )
6676 }
6677
6678 fn selection_replacement_ranges(
6679 &self,
6680 range: Range<OffsetUtf16>,
6681 cx: &AppContext,
6682 ) -> Vec<Range<OffsetUtf16>> {
6683 let selections = self.selections.all::<OffsetUtf16>(cx);
6684 let newest_selection = selections
6685 .iter()
6686 .max_by_key(|selection| selection.id)
6687 .unwrap();
6688 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
6689 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
6690 let snapshot = self.buffer.read(cx).read(cx);
6691 selections
6692 .into_iter()
6693 .map(|mut selection| {
6694 selection.start.0 =
6695 (selection.start.0 as isize).saturating_add(start_delta) as usize;
6696 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
6697 snapshot.clip_offset_utf16(selection.start, Bias::Left)
6698 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
6699 })
6700 .collect()
6701 }
6702
6703 fn report_event(&self, name: &str, cx: &AppContext) {
6704 if let Some((project, file)) = self.project.as_ref().zip(
6705 self.buffer
6706 .read(cx)
6707 .as_singleton()
6708 .and_then(|b| b.read(cx).file()),
6709 ) {
6710 let settings = cx.global::<Settings>();
6711
6712 let extension = Path::new(file.file_name(cx))
6713 .extension()
6714 .and_then(|e| e.to_str());
6715 project.read(cx).client().report_event(
6716 name,
6717 json!({ "File Extension": extension, "Vim Mode": settings.vim_mode }),
6718 settings.telemetry(),
6719 );
6720 }
6721 }
6722
6723 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
6724 /// with each line being an array of {text, highlight} objects.
6725 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
6726 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
6727 return;
6728 };
6729
6730 #[derive(Serialize)]
6731 struct Chunk<'a> {
6732 text: String,
6733 highlight: Option<&'a str>,
6734 }
6735
6736 let snapshot = buffer.read(cx).snapshot();
6737 let range = self
6738 .selected_text_range(cx)
6739 .and_then(|selected_range| {
6740 if selected_range.is_empty() {
6741 None
6742 } else {
6743 Some(selected_range)
6744 }
6745 })
6746 .unwrap_or_else(|| 0..snapshot.len());
6747
6748 let chunks = snapshot.chunks(range, true);
6749 let mut lines = Vec::new();
6750 let mut line: VecDeque<Chunk> = VecDeque::new();
6751
6752 let theme = &cx.global::<Settings>().theme.editor.syntax;
6753
6754 for chunk in chunks {
6755 let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme));
6756 let mut chunk_lines = chunk.text.split("\n").peekable();
6757 while let Some(text) = chunk_lines.next() {
6758 let mut merged_with_last_token = false;
6759 if let Some(last_token) = line.back_mut() {
6760 if last_token.highlight == highlight {
6761 last_token.text.push_str(text);
6762 merged_with_last_token = true;
6763 }
6764 }
6765
6766 if !merged_with_last_token {
6767 line.push_back(Chunk {
6768 text: text.into(),
6769 highlight,
6770 });
6771 }
6772
6773 if chunk_lines.peek().is_some() {
6774 if line.len() > 1 && line.front().unwrap().text.is_empty() {
6775 line.pop_front();
6776 }
6777 if line.len() > 1 && line.back().unwrap().text.is_empty() {
6778 line.pop_back();
6779 }
6780
6781 lines.push(mem::take(&mut line));
6782 }
6783 }
6784 }
6785
6786 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else { return; };
6787 cx.write_to_clipboard(ClipboardItem::new(lines));
6788 }
6789}
6790
6791fn consume_contiguous_rows(
6792 contiguous_row_selections: &mut Vec<Selection<Point>>,
6793 selection: &Selection<Point>,
6794 display_map: &DisplaySnapshot,
6795 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
6796) -> (u32, u32) {
6797 contiguous_row_selections.push(selection.clone());
6798 let start_row = selection.start.row;
6799 let mut end_row = ending_row(selection, display_map);
6800
6801 while let Some(next_selection) = selections.peek() {
6802 if next_selection.start.row <= end_row {
6803 end_row = ending_row(next_selection, display_map);
6804 contiguous_row_selections.push(selections.next().unwrap().clone());
6805 } else {
6806 break;
6807 }
6808 }
6809 (start_row, end_row)
6810}
6811
6812fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
6813 if next_selection.end.column > 0 || next_selection.is_empty() {
6814 display_map.next_line_boundary(next_selection.end).0.row + 1
6815 } else {
6816 next_selection.end.row
6817 }
6818}
6819
6820impl EditorSnapshot {
6821 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
6822 self.display_snapshot.buffer_snapshot.language_at(position)
6823 }
6824
6825 pub fn is_focused(&self) -> bool {
6826 self.is_focused
6827 }
6828
6829 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
6830 self.placeholder_text.as_ref()
6831 }
6832
6833 pub fn scroll_position(&self) -> Vector2F {
6834 self.scroll_anchor.scroll_position(&self.display_snapshot)
6835 }
6836}
6837
6838impl Deref for EditorSnapshot {
6839 type Target = DisplaySnapshot;
6840
6841 fn deref(&self) -> &Self::Target {
6842 &self.display_snapshot
6843 }
6844}
6845
6846#[derive(Clone, Debug, PartialEq, Eq)]
6847pub enum Event {
6848 InputIgnored {
6849 text: Arc<str>,
6850 },
6851 ExcerptsAdded {
6852 buffer: ModelHandle<Buffer>,
6853 predecessor: ExcerptId,
6854 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
6855 },
6856 ExcerptsRemoved {
6857 ids: Vec<ExcerptId>,
6858 },
6859 BufferEdited,
6860 Edited,
6861 Reparsed,
6862 Blurred,
6863 DirtyChanged,
6864 Saved,
6865 TitleChanged,
6866 SelectionsChanged {
6867 local: bool,
6868 },
6869 ScrollPositionChanged {
6870 local: bool,
6871 },
6872 Closed,
6873}
6874
6875pub struct EditorFocused(pub ViewHandle<Editor>);
6876pub struct EditorBlurred(pub ViewHandle<Editor>);
6877pub struct EditorReleased(pub WeakViewHandle<Editor>);
6878
6879impl Entity for Editor {
6880 type Event = Event;
6881
6882 fn release(&mut self, cx: &mut AppContext) {
6883 cx.emit_global(EditorReleased(self.handle.clone()));
6884 }
6885}
6886
6887impl View for Editor {
6888 fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
6889 let style = self.style(cx);
6890 let font_changed = self.display_map.update(cx, |map, cx| {
6891 map.set_fold_ellipses_color(style.folds.ellipses.text_color);
6892 map.set_font(style.text.font_id, style.text.font_size, cx)
6893 });
6894
6895 if font_changed {
6896 let handle = self.handle.clone();
6897 cx.defer(move |cx| {
6898 if let Some(editor) = handle.upgrade(cx) {
6899 editor.update(cx, |editor, cx| {
6900 hide_hover(editor, &HideHover, cx);
6901 hide_link_definition(editor, cx);
6902 })
6903 }
6904 });
6905 }
6906
6907 Stack::new()
6908 .with_child(EditorElement::new(self.handle.clone(), style.clone()).boxed())
6909 .with_child(ChildView::new(&self.mouse_context_menu, cx).boxed())
6910 .boxed()
6911 }
6912
6913 fn ui_name() -> &'static str {
6914 "Editor"
6915 }
6916
6917 fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
6918 if cx.is_self_focused() {
6919 let focused_event = EditorFocused(cx.handle());
6920 cx.emit_global(focused_event);
6921 }
6922 if let Some(rename) = self.pending_rename.as_ref() {
6923 cx.focus(&rename.editor);
6924 } else {
6925 if !self.focused {
6926 self.blink_manager.update(cx, BlinkManager::enable);
6927 }
6928 self.focused = true;
6929 self.buffer.update(cx, |buffer, cx| {
6930 buffer.finalize_last_transaction(cx);
6931 if self.leader_replica_id.is_none() {
6932 buffer.set_active_selections(
6933 &self.selections.disjoint_anchors(),
6934 self.selections.line_mode,
6935 self.cursor_shape,
6936 cx,
6937 );
6938 }
6939 });
6940 }
6941 }
6942
6943 fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
6944 let blurred_event = EditorBlurred(cx.handle());
6945 cx.emit_global(blurred_event);
6946 self.focused = false;
6947 self.blink_manager.update(cx, BlinkManager::disable);
6948 self.buffer
6949 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
6950 self.hide_context_menu(cx);
6951 hide_hover(self, &HideHover, cx);
6952 cx.emit(Event::Blurred);
6953 cx.notify();
6954 }
6955
6956 fn modifiers_changed(
6957 &mut self,
6958 event: &gpui::platform::ModifiersChangedEvent,
6959 cx: &mut ViewContext<Self>,
6960 ) -> bool {
6961 let pending_selection = self.has_pending_selection();
6962
6963 if let Some(point) = self.link_go_to_definition_state.last_mouse_location.clone() {
6964 if event.cmd && !pending_selection {
6965 let snapshot = self.snapshot(cx);
6966 let kind = if event.shift {
6967 LinkDefinitionKind::Type
6968 } else {
6969 LinkDefinitionKind::Symbol
6970 };
6971
6972 show_link_definition(kind, self, point, snapshot, cx);
6973 return false;
6974 }
6975 }
6976
6977 {
6978 if self.link_go_to_definition_state.symbol_range.is_some()
6979 || !self.link_go_to_definition_state.definitions.is_empty()
6980 {
6981 self.link_go_to_definition_state.symbol_range.take();
6982 self.link_go_to_definition_state.definitions.clear();
6983 cx.notify();
6984 }
6985
6986 self.link_go_to_definition_state.task = None;
6987
6988 self.clear_text_highlights::<LinkGoToDefinitionState>(cx);
6989 }
6990
6991 false
6992 }
6993
6994 fn keymap_context(&self, _: &AppContext) -> KeymapContext {
6995 let mut context = Self::default_keymap_context();
6996 let mode = match self.mode {
6997 EditorMode::SingleLine => "single_line",
6998 EditorMode::AutoHeight { .. } => "auto_height",
6999 EditorMode::Full => "full",
7000 };
7001 context.add_key("mode", mode);
7002 if self.pending_rename.is_some() {
7003 context.add_identifier("renaming");
7004 }
7005 match self.context_menu.as_ref() {
7006 Some(ContextMenu::Completions(_)) => context.add_identifier("showing_completions"),
7007 Some(ContextMenu::CodeActions(_)) => context.add_identifier("showing_code_actions"),
7008 None => {}
7009 }
7010
7011 for layer in self.keymap_context_layers.values() {
7012 context.extend(layer);
7013 }
7014
7015 context
7016 }
7017
7018 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
7019 Some(
7020 self.buffer
7021 .read(cx)
7022 .read(cx)
7023 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
7024 .collect(),
7025 )
7026 }
7027
7028 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
7029 // Prevent the IME menu from appearing when holding down an alphabetic key
7030 // while input is disabled.
7031 if !self.input_enabled {
7032 return None;
7033 }
7034
7035 let range = self.selections.newest::<OffsetUtf16>(cx).range();
7036 Some(range.start.0..range.end.0)
7037 }
7038
7039 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
7040 let snapshot = self.buffer.read(cx).read(cx);
7041 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
7042 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
7043 }
7044
7045 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
7046 self.clear_text_highlights::<InputComposition>(cx);
7047 self.ime_transaction.take();
7048 }
7049
7050 fn replace_text_in_range(
7051 &mut self,
7052 range_utf16: Option<Range<usize>>,
7053 text: &str,
7054 cx: &mut ViewContext<Self>,
7055 ) {
7056 self.transact(cx, |this, cx| {
7057 if this.input_enabled {
7058 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
7059 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
7060 Some(this.selection_replacement_ranges(range_utf16, cx))
7061 } else {
7062 this.marked_text_ranges(cx)
7063 };
7064
7065 if let Some(new_selected_ranges) = new_selected_ranges {
7066 this.change_selections(None, cx, |selections| {
7067 selections.select_ranges(new_selected_ranges)
7068 });
7069 }
7070 }
7071
7072 this.handle_input(text, cx);
7073 });
7074
7075 if !self.input_enabled {
7076 return;
7077 }
7078
7079 if let Some(transaction) = self.ime_transaction {
7080 self.buffer.update(cx, |buffer, cx| {
7081 buffer.group_until_transaction(transaction, cx);
7082 });
7083 }
7084
7085 self.unmark_text(cx);
7086 }
7087
7088 fn replace_and_mark_text_in_range(
7089 &mut self,
7090 range_utf16: Option<Range<usize>>,
7091 text: &str,
7092 new_selected_range_utf16: Option<Range<usize>>,
7093 cx: &mut ViewContext<Self>,
7094 ) {
7095 if !self.input_enabled {
7096 return;
7097 }
7098
7099 let transaction = self.transact(cx, |this, cx| {
7100 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
7101 let snapshot = this.buffer.read(cx).read(cx);
7102 if let Some(relative_range_utf16) = range_utf16.as_ref() {
7103 for marked_range in &mut marked_ranges {
7104 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
7105 marked_range.start.0 += relative_range_utf16.start;
7106 marked_range.start =
7107 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
7108 marked_range.end =
7109 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
7110 }
7111 }
7112 Some(marked_ranges)
7113 } else if let Some(range_utf16) = range_utf16 {
7114 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
7115 Some(this.selection_replacement_ranges(range_utf16, cx))
7116 } else {
7117 None
7118 };
7119
7120 if let Some(ranges) = ranges_to_replace {
7121 this.change_selections(None, cx, |s| s.select_ranges(ranges));
7122 }
7123
7124 let marked_ranges = {
7125 let snapshot = this.buffer.read(cx).read(cx);
7126 this.selections
7127 .disjoint_anchors()
7128 .iter()
7129 .map(|selection| {
7130 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
7131 })
7132 .collect::<Vec<_>>()
7133 };
7134
7135 if text.is_empty() {
7136 this.unmark_text(cx);
7137 } else {
7138 this.highlight_text::<InputComposition>(
7139 marked_ranges.clone(),
7140 this.style(cx).composition_mark,
7141 cx,
7142 );
7143 }
7144
7145 this.handle_input(text, cx);
7146
7147 if let Some(new_selected_range) = new_selected_range_utf16 {
7148 let snapshot = this.buffer.read(cx).read(cx);
7149 let new_selected_ranges = marked_ranges
7150 .into_iter()
7151 .map(|marked_range| {
7152 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
7153 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
7154 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
7155 snapshot.clip_offset_utf16(new_start, Bias::Left)
7156 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
7157 })
7158 .collect::<Vec<_>>();
7159
7160 drop(snapshot);
7161 this.change_selections(None, cx, |selections| {
7162 selections.select_ranges(new_selected_ranges)
7163 });
7164 }
7165 });
7166
7167 self.ime_transaction = self.ime_transaction.or(transaction);
7168 if let Some(transaction) = self.ime_transaction {
7169 self.buffer.update(cx, |buffer, cx| {
7170 buffer.group_until_transaction(transaction, cx);
7171 });
7172 }
7173
7174 if self.text_highlights::<InputComposition>(cx).is_none() {
7175 self.ime_transaction.take();
7176 }
7177 }
7178}
7179
7180fn build_style(
7181 settings: &Settings,
7182 get_field_editor_theme: Option<&GetFieldEditorTheme>,
7183 override_text_style: Option<&OverrideTextStyle>,
7184 cx: &AppContext,
7185) -> EditorStyle {
7186 let font_cache = cx.font_cache();
7187
7188 let mut theme = settings.theme.editor.clone();
7189 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
7190 let field_editor_theme = get_field_editor_theme(&settings.theme);
7191 theme.text_color = field_editor_theme.text.color;
7192 theme.selection = field_editor_theme.selection;
7193 theme.background = field_editor_theme
7194 .container
7195 .background_color
7196 .unwrap_or_default();
7197 EditorStyle {
7198 text: field_editor_theme.text,
7199 placeholder_text: field_editor_theme.placeholder_text,
7200 theme,
7201 }
7202 } else {
7203 let font_family_id = settings.buffer_font_family;
7204 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
7205 let font_properties = Default::default();
7206 let font_id = font_cache
7207 .select_font(font_family_id, &font_properties)
7208 .unwrap();
7209 let font_size = settings.buffer_font_size;
7210 EditorStyle {
7211 text: TextStyle {
7212 color: settings.theme.editor.text_color,
7213 font_family_name,
7214 font_family_id,
7215 font_id,
7216 font_size,
7217 font_properties,
7218 underline: Default::default(),
7219 },
7220 placeholder_text: None,
7221 theme,
7222 }
7223 };
7224
7225 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
7226 if let Some(highlighted) = style
7227 .text
7228 .clone()
7229 .highlight(highlight_style, font_cache)
7230 .log_err()
7231 {
7232 style.text = highlighted;
7233 }
7234 }
7235
7236 style
7237}
7238
7239trait SelectionExt {
7240 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
7241 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
7242 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
7243 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
7244 -> Range<u32>;
7245}
7246
7247impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
7248 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
7249 let start = self.start.to_point(buffer);
7250 let end = self.end.to_point(buffer);
7251 if self.reversed {
7252 end..start
7253 } else {
7254 start..end
7255 }
7256 }
7257
7258 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
7259 let start = self.start.to_offset(buffer);
7260 let end = self.end.to_offset(buffer);
7261 if self.reversed {
7262 end..start
7263 } else {
7264 start..end
7265 }
7266 }
7267
7268 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
7269 let start = self
7270 .start
7271 .to_point(&map.buffer_snapshot)
7272 .to_display_point(map);
7273 let end = self
7274 .end
7275 .to_point(&map.buffer_snapshot)
7276 .to_display_point(map);
7277 if self.reversed {
7278 end..start
7279 } else {
7280 start..end
7281 }
7282 }
7283
7284 fn spanned_rows(
7285 &self,
7286 include_end_if_at_line_start: bool,
7287 map: &DisplaySnapshot,
7288 ) -> Range<u32> {
7289 let start = self.start.to_point(&map.buffer_snapshot);
7290 let mut end = self.end.to_point(&map.buffer_snapshot);
7291 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
7292 end.row -= 1;
7293 }
7294
7295 let buffer_start = map.prev_line_boundary(start).0;
7296 let buffer_end = map.next_line_boundary(end).0;
7297 buffer_start.row..buffer_end.row + 1
7298 }
7299}
7300
7301impl<T: InvalidationRegion> InvalidationStack<T> {
7302 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
7303 where
7304 S: Clone + ToOffset,
7305 {
7306 while let Some(region) = self.last() {
7307 let all_selections_inside_invalidation_ranges =
7308 if selections.len() == region.ranges().len() {
7309 selections
7310 .iter()
7311 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
7312 .all(|(selection, invalidation_range)| {
7313 let head = selection.head().to_offset(buffer);
7314 invalidation_range.start <= head && invalidation_range.end >= head
7315 })
7316 } else {
7317 false
7318 };
7319
7320 if all_selections_inside_invalidation_ranges {
7321 break;
7322 } else {
7323 self.pop();
7324 }
7325 }
7326 }
7327}
7328
7329impl<T> Default for InvalidationStack<T> {
7330 fn default() -> Self {
7331 Self(Default::default())
7332 }
7333}
7334
7335impl<T> Deref for InvalidationStack<T> {
7336 type Target = Vec<T>;
7337
7338 fn deref(&self) -> &Self::Target {
7339 &self.0
7340 }
7341}
7342
7343impl<T> DerefMut for InvalidationStack<T> {
7344 fn deref_mut(&mut self) -> &mut Self::Target {
7345 &mut self.0
7346 }
7347}
7348
7349impl InvalidationRegion for SnippetState {
7350 fn ranges(&self) -> &[Range<Anchor>] {
7351 &self.ranges[self.active_index]
7352 }
7353}
7354
7355impl Deref for EditorStyle {
7356 type Target = theme::Editor;
7357
7358 fn deref(&self) -> &Self::Target {
7359 &self.theme
7360 }
7361}
7362
7363pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
7364 let mut highlighted_lines = Vec::new();
7365 for line in diagnostic.message.lines() {
7366 highlighted_lines.push(highlight_diagnostic_message(line));
7367 }
7368
7369 Arc::new(move |cx: &mut BlockContext| {
7370 let settings = cx.global::<Settings>();
7371 let theme = &settings.theme.editor;
7372 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
7373 let font_size = (style.text_scale_factor * settings.buffer_font_size).round();
7374 Flex::column()
7375 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
7376 Label::new(
7377 line.clone(),
7378 style.message.clone().with_font_size(font_size),
7379 )
7380 .with_highlights(highlights.clone())
7381 .contained()
7382 .with_margin_left(cx.anchor_x)
7383 .boxed()
7384 }))
7385 .aligned()
7386 .left()
7387 .boxed()
7388 })
7389}
7390
7391pub fn highlight_diagnostic_message(message: &str) -> (String, Vec<usize>) {
7392 let mut message_without_backticks = String::new();
7393 let mut prev_offset = 0;
7394 let mut inside_block = false;
7395 let mut highlights = Vec::new();
7396 for (match_ix, (offset, _)) in message
7397 .match_indices('`')
7398 .chain([(message.len(), "")])
7399 .enumerate()
7400 {
7401 message_without_backticks.push_str(&message[prev_offset..offset]);
7402 if inside_block {
7403 highlights.extend(prev_offset - match_ix..offset - match_ix);
7404 }
7405
7406 inside_block = !inside_block;
7407 prev_offset = offset + 1;
7408 }
7409
7410 (message_without_backticks, highlights)
7411}
7412
7413pub fn diagnostic_style(
7414 severity: DiagnosticSeverity,
7415 valid: bool,
7416 theme: &theme::Editor,
7417) -> DiagnosticStyle {
7418 match (severity, valid) {
7419 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
7420 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
7421 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
7422 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
7423 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
7424 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
7425 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
7426 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
7427 _ => theme.invalid_hint_diagnostic.clone(),
7428 }
7429}
7430
7431pub fn combine_syntax_and_fuzzy_match_highlights(
7432 text: &str,
7433 default_style: HighlightStyle,
7434 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
7435 match_indices: &[usize],
7436) -> Vec<(Range<usize>, HighlightStyle)> {
7437 let mut result = Vec::new();
7438 let mut match_indices = match_indices.iter().copied().peekable();
7439
7440 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
7441 {
7442 syntax_highlight.weight = None;
7443
7444 // Add highlights for any fuzzy match characters before the next
7445 // syntax highlight range.
7446 while let Some(&match_index) = match_indices.peek() {
7447 if match_index >= range.start {
7448 break;
7449 }
7450 match_indices.next();
7451 let end_index = char_ix_after(match_index, text);
7452 let mut match_style = default_style;
7453 match_style.weight = Some(fonts::Weight::BOLD);
7454 result.push((match_index..end_index, match_style));
7455 }
7456
7457 if range.start == usize::MAX {
7458 break;
7459 }
7460
7461 // Add highlights for any fuzzy match characters within the
7462 // syntax highlight range.
7463 let mut offset = range.start;
7464 while let Some(&match_index) = match_indices.peek() {
7465 if match_index >= range.end {
7466 break;
7467 }
7468
7469 match_indices.next();
7470 if match_index > offset {
7471 result.push((offset..match_index, syntax_highlight));
7472 }
7473
7474 let mut end_index = char_ix_after(match_index, text);
7475 while let Some(&next_match_index) = match_indices.peek() {
7476 if next_match_index == end_index && next_match_index < range.end {
7477 end_index = char_ix_after(next_match_index, text);
7478 match_indices.next();
7479 } else {
7480 break;
7481 }
7482 }
7483
7484 let mut match_style = syntax_highlight;
7485 match_style.weight = Some(fonts::Weight::BOLD);
7486 result.push((match_index..end_index, match_style));
7487 offset = end_index;
7488 }
7489
7490 if offset < range.end {
7491 result.push((offset..range.end, syntax_highlight));
7492 }
7493 }
7494
7495 fn char_ix_after(ix: usize, text: &str) -> usize {
7496 ix + text[ix..].chars().next().unwrap().len_utf8()
7497 }
7498
7499 result
7500}
7501
7502pub fn styled_runs_for_code_label<'a>(
7503 label: &'a CodeLabel,
7504 syntax_theme: &'a theme::SyntaxTheme,
7505) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
7506 let fade_out = HighlightStyle {
7507 fade_out: Some(0.35),
7508 ..Default::default()
7509 };
7510
7511 let mut prev_end = label.filter_range.end;
7512 label
7513 .runs
7514 .iter()
7515 .enumerate()
7516 .flat_map(move |(ix, (range, highlight_id))| {
7517 let style = if let Some(style) = highlight_id.style(syntax_theme) {
7518 style
7519 } else {
7520 return Default::default();
7521 };
7522 let mut muted_style = style;
7523 muted_style.highlight(fade_out);
7524
7525 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
7526 if range.start >= label.filter_range.end {
7527 if range.start > prev_end {
7528 runs.push((prev_end..range.start, fade_out));
7529 }
7530 runs.push((range.clone(), muted_style));
7531 } else if range.end <= label.filter_range.end {
7532 runs.push((range.clone(), style));
7533 } else {
7534 runs.push((range.start..label.filter_range.end, style));
7535 runs.push((label.filter_range.end..range.end, muted_style));
7536 }
7537 prev_end = cmp::max(prev_end, range.end);
7538
7539 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
7540 runs.push((prev_end..label.text.len(), fade_out));
7541 }
7542
7543 runs
7544 })
7545}
7546
7547pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
7548 let mut index = 0;
7549 let mut codepoints = text.char_indices().peekable();
7550
7551 std::iter::from_fn(move || {
7552 let start_index = index;
7553 while let Some((new_index, codepoint)) = codepoints.next() {
7554 index = new_index + codepoint.len_utf8();
7555 let current_upper = codepoint.is_uppercase();
7556 let next_upper = codepoints
7557 .peek()
7558 .map(|(_, c)| c.is_uppercase())
7559 .unwrap_or(false);
7560
7561 if !current_upper && next_upper {
7562 return Some(&text[start_index..index]);
7563 }
7564 }
7565
7566 index = text.len();
7567 if start_index < text.len() {
7568 return Some(&text[start_index..]);
7569 }
7570 None
7571 })
7572 .flat_map(|word| word.split_inclusive('_'))
7573}
7574
7575trait RangeToAnchorExt {
7576 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
7577}
7578
7579impl<T: ToOffset> RangeToAnchorExt for Range<T> {
7580 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
7581 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
7582 }
7583}