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