1mod blink_manager;
2pub mod display_map;
3mod element;
4
5mod git;
6mod highlight_matching_bracket;
7mod hover_popover;
8pub mod items;
9mod link_go_to_definition;
10mod mouse_context_menu;
11pub mod movement;
12mod multi_buffer;
13mod persistence;
14pub mod scroll;
15pub mod selections_collection;
16
17#[cfg(test)]
18mod editor_tests;
19#[cfg(any(test, feature = "test-support"))]
20pub mod test;
21
22use aho_corasick::AhoCorasick;
23use anyhow::{anyhow, Result};
24use blink_manager::BlinkManager;
25use clock::ReplicaId;
26use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
27use copilot::Copilot;
28pub use display_map::DisplayPoint;
29use display_map::*;
30pub use element::*;
31use futures::FutureExt;
32use fuzzy::{StringMatch, StringMatchCandidate};
33use gpui::{
34 actions,
35 color::Color,
36 elements::*,
37 executor,
38 fonts::{self, HighlightStyle, TextStyle},
39 geometry::vector::Vector2F,
40 impl_actions, impl_internal_actions,
41 keymap_matcher::KeymapContext,
42 platform::{CursorStyle, MouseButton},
43 serde_json::{self, json},
44 AnyViewHandle, AppContext, AsyncAppContext, ClipboardItem, Element, ElementBox, Entity,
45 ModelHandle, RenderContext, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle,
46};
47use highlight_matching_bracket::refresh_matching_bracket_highlights;
48use hover_popover::{hide_hover, HideHover, HoverState};
49pub use items::MAX_TAB_TITLE_LEN;
50use itertools::Itertools;
51pub use language::{char_kind, CharKind};
52use language::{
53 AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel, Completion, CursorShape,
54 Diagnostic, DiagnosticSeverity, IndentKind, IndentSize, Language, OffsetRangeExt, OffsetUtf16,
55 Point, Rope, Selection, SelectionGoal, TransactionId,
56};
57use link_go_to_definition::{
58 hide_link_definition, show_link_definition, LinkDefinitionKind, LinkGoToDefinitionState,
59};
60pub use multi_buffer::{
61 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
62 ToPoint,
63};
64use multi_buffer::{MultiBufferChunks, ToOffsetUtf16};
65use ordered_float::OrderedFloat;
66use project::{FormatTrigger, Location, LocationLink, Project, ProjectPath, ProjectTransaction};
67use scroll::{
68 autoscroll::Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide,
69};
70use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection};
71use serde::{Deserialize, Serialize};
72use settings::Settings;
73use smallvec::SmallVec;
74use snippet::Snippet;
75use std::{
76 any::TypeId,
77 borrow::Cow,
78 cmp::{self, Ordering, Reverse},
79 mem,
80 num::NonZeroU32,
81 ops::{Deref, DerefMut, Range},
82 path::Path,
83 sync::Arc,
84 time::{Duration, Instant},
85};
86pub use sum_tree::Bias;
87use theme::{DiagnosticStyle, Theme};
88use util::{post_inc, RangeExt, ResultExt, TryFutureExt};
89use workspace::{ItemNavHistory, ViewId, Workspace, WorkspaceId};
90
91use crate::git::diff_hunk_to_display;
92
93const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
94const MAX_LINE_LEN: usize = 1024;
95const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
96const MAX_SELECTION_HISTORY_LEN: usize = 1024;
97const COPILOT_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
98
99pub const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
100
101#[derive(Clone, Deserialize, PartialEq, Default)]
102pub struct SelectNext {
103 #[serde(default)]
104 pub replace_newest: bool,
105}
106
107#[derive(Clone, PartialEq)]
108pub struct Select(pub SelectPhase);
109
110#[derive(Clone, Debug, PartialEq)]
111pub struct Jump {
112 path: ProjectPath,
113 position: Point,
114 anchor: language::Anchor,
115}
116
117#[derive(Clone, Deserialize, PartialEq)]
118pub struct SelectToBeginningOfLine {
119 #[serde(default)]
120 stop_at_soft_wraps: bool,
121}
122
123#[derive(Clone, Default, Deserialize, PartialEq)]
124pub struct MovePageUp {
125 #[serde(default)]
126 center_cursor: bool,
127}
128
129#[derive(Clone, Default, Deserialize, PartialEq)]
130pub struct MovePageDown {
131 #[serde(default)]
132 center_cursor: bool,
133}
134
135#[derive(Clone, Deserialize, PartialEq)]
136pub struct SelectToEndOfLine {
137 #[serde(default)]
138 stop_at_soft_wraps: bool,
139}
140
141#[derive(Clone, Deserialize, PartialEq)]
142pub struct ToggleCodeActions {
143 #[serde(default)]
144 pub deployed_from_indicator: bool,
145}
146
147#[derive(Clone, Default, Deserialize, PartialEq)]
148pub struct ConfirmCompletion {
149 #[serde(default)]
150 pub item_ix: Option<usize>,
151}
152
153#[derive(Clone, Default, Deserialize, PartialEq)]
154pub struct ConfirmCodeAction {
155 #[serde(default)]
156 pub item_ix: Option<usize>,
157}
158
159#[derive(Clone, Default, Deserialize, PartialEq)]
160pub struct ToggleComments {
161 #[serde(default)]
162 pub advance_downwards: bool,
163}
164
165#[derive(Clone, Default, Deserialize, PartialEq)]
166pub struct FoldAt {
167 pub buffer_row: u32,
168}
169
170#[derive(Clone, Default, Deserialize, PartialEq)]
171pub struct UnfoldAt {
172 pub buffer_row: u32,
173}
174
175#[derive(Clone, Default, Deserialize, PartialEq)]
176pub struct GutterHover {
177 pub hovered: bool,
178}
179
180actions!(
181 editor,
182 [
183 Cancel,
184 Backspace,
185 Delete,
186 Newline,
187 NewlineBelow,
188 GoToDiagnostic,
189 GoToPrevDiagnostic,
190 GoToHunk,
191 GoToPrevHunk,
192 Indent,
193 Outdent,
194 DeleteLine,
195 DeleteToPreviousWordStart,
196 DeleteToPreviousSubwordStart,
197 DeleteToNextWordEnd,
198 DeleteToNextSubwordEnd,
199 DeleteToBeginningOfLine,
200 DeleteToEndOfLine,
201 CutToEndOfLine,
202 DuplicateLine,
203 MoveLineUp,
204 MoveLineDown,
205 Transpose,
206 Cut,
207 Copy,
208 Paste,
209 Undo,
210 Redo,
211 MoveUp,
212 PageUp,
213 MoveDown,
214 PageDown,
215 MoveLeft,
216 MoveRight,
217 MoveToPreviousWordStart,
218 MoveToPreviousSubwordStart,
219 MoveToNextWordEnd,
220 MoveToNextSubwordEnd,
221 MoveToBeginningOfLine,
222 MoveToEndOfLine,
223 MoveToBeginning,
224 MoveToEnd,
225 SelectUp,
226 SelectDown,
227 SelectLeft,
228 SelectRight,
229 SelectToPreviousWordStart,
230 SelectToPreviousSubwordStart,
231 SelectToNextWordEnd,
232 SelectToNextSubwordEnd,
233 SelectToBeginning,
234 SelectToEnd,
235 SelectAll,
236 SelectLine,
237 SplitSelectionIntoLines,
238 AddSelectionAbove,
239 AddSelectionBelow,
240 Tab,
241 TabPrev,
242 ShowCharacterPalette,
243 SelectLargerSyntaxNode,
244 SelectSmallerSyntaxNode,
245 GoToDefinition,
246 GoToTypeDefinition,
247 MoveToEnclosingBracket,
248 UndoSelection,
249 RedoSelection,
250 FindAllReferences,
251 Rename,
252 ConfirmRename,
253 Fold,
254 UnfoldLines,
255 FoldSelectedRanges,
256 ShowCompletions,
257 OpenExcerpts,
258 RestartLanguageServer,
259 Hover,
260 Format,
261 ToggleSoftWrap,
262 RevealInFinder,
263 CopyPath,
264 CopyRelativePath,
265 CopyHighlightJson
266 ]
267);
268
269impl_actions!(
270 editor,
271 [
272 SelectNext,
273 SelectToBeginningOfLine,
274 SelectToEndOfLine,
275 ToggleCodeActions,
276 MovePageUp,
277 MovePageDown,
278 ConfirmCompletion,
279 ConfirmCodeAction,
280 ToggleComments,
281 FoldAt,
282 UnfoldAt,
283 GutterHover
284 ]
285);
286
287impl_internal_actions!(editor, [Select, Jump]);
288
289enum DocumentHighlightRead {}
290enum DocumentHighlightWrite {}
291enum InputComposition {}
292
293#[derive(Copy, Clone, PartialEq, Eq)]
294pub enum Direction {
295 Prev,
296 Next,
297}
298
299pub fn init(cx: &mut AppContext) {
300 cx.add_action(Editor::new_file);
301 cx.add_action(Editor::select);
302 cx.add_action(Editor::cancel);
303 cx.add_action(Editor::newline);
304 cx.add_action(Editor::newline_below);
305 cx.add_action(Editor::backspace);
306 cx.add_action(Editor::delete);
307 cx.add_action(Editor::tab);
308 cx.add_action(Editor::tab_prev);
309 cx.add_action(Editor::indent);
310 cx.add_action(Editor::outdent);
311 cx.add_action(Editor::delete_line);
312 cx.add_action(Editor::delete_to_previous_word_start);
313 cx.add_action(Editor::delete_to_previous_subword_start);
314 cx.add_action(Editor::delete_to_next_word_end);
315 cx.add_action(Editor::delete_to_next_subword_end);
316 cx.add_action(Editor::delete_to_beginning_of_line);
317 cx.add_action(Editor::delete_to_end_of_line);
318 cx.add_action(Editor::cut_to_end_of_line);
319 cx.add_action(Editor::duplicate_line);
320 cx.add_action(Editor::move_line_up);
321 cx.add_action(Editor::move_line_down);
322 cx.add_action(Editor::transpose);
323 cx.add_action(Editor::cut);
324 cx.add_action(Editor::copy);
325 cx.add_action(Editor::paste);
326 cx.add_action(Editor::undo);
327 cx.add_action(Editor::redo);
328 cx.add_action(Editor::move_up);
329 cx.add_action(Editor::move_page_up);
330 cx.add_action(Editor::move_down);
331 cx.add_action(Editor::move_page_down);
332 cx.add_action(Editor::next_screen);
333 cx.add_action(Editor::move_left);
334 cx.add_action(Editor::move_right);
335 cx.add_action(Editor::move_to_previous_word_start);
336 cx.add_action(Editor::move_to_previous_subword_start);
337 cx.add_action(Editor::move_to_next_word_end);
338 cx.add_action(Editor::move_to_next_subword_end);
339 cx.add_action(Editor::move_to_beginning_of_line);
340 cx.add_action(Editor::move_to_end_of_line);
341 cx.add_action(Editor::move_to_beginning);
342 cx.add_action(Editor::move_to_end);
343 cx.add_action(Editor::select_up);
344 cx.add_action(Editor::select_down);
345 cx.add_action(Editor::select_left);
346 cx.add_action(Editor::select_right);
347 cx.add_action(Editor::select_to_previous_word_start);
348 cx.add_action(Editor::select_to_previous_subword_start);
349 cx.add_action(Editor::select_to_next_word_end);
350 cx.add_action(Editor::select_to_next_subword_end);
351 cx.add_action(Editor::select_to_beginning_of_line);
352 cx.add_action(Editor::select_to_end_of_line);
353 cx.add_action(Editor::select_to_beginning);
354 cx.add_action(Editor::select_to_end);
355 cx.add_action(Editor::select_all);
356 cx.add_action(Editor::select_line);
357 cx.add_action(Editor::split_selection_into_lines);
358 cx.add_action(Editor::add_selection_above);
359 cx.add_action(Editor::add_selection_below);
360 cx.add_action(Editor::select_next);
361 cx.add_action(Editor::toggle_comments);
362 cx.add_action(Editor::select_larger_syntax_node);
363 cx.add_action(Editor::select_smaller_syntax_node);
364 cx.add_action(Editor::move_to_enclosing_bracket);
365 cx.add_action(Editor::undo_selection);
366 cx.add_action(Editor::redo_selection);
367 cx.add_action(Editor::go_to_diagnostic);
368 cx.add_action(Editor::go_to_prev_diagnostic);
369 cx.add_action(Editor::go_to_hunk);
370 cx.add_action(Editor::go_to_prev_hunk);
371 cx.add_action(Editor::go_to_definition);
372 cx.add_action(Editor::go_to_type_definition);
373 cx.add_action(Editor::fold);
374 cx.add_action(Editor::fold_at);
375 cx.add_action(Editor::unfold_lines);
376 cx.add_action(Editor::unfold_at);
377 cx.add_action(Editor::gutter_hover);
378 cx.add_action(Editor::fold_selected_ranges);
379 cx.add_action(Editor::show_completions);
380 cx.add_action(Editor::toggle_code_actions);
381 cx.add_action(Editor::open_excerpts);
382 cx.add_action(Editor::jump);
383 cx.add_action(Editor::toggle_soft_wrap);
384 cx.add_action(Editor::reveal_in_finder);
385 cx.add_action(Editor::copy_path);
386 cx.add_action(Editor::copy_relative_path);
387 cx.add_action(Editor::copy_highlight_json);
388 cx.add_async_action(Editor::format);
389 cx.add_action(Editor::restart_language_server);
390 cx.add_action(Editor::show_character_palette);
391 cx.add_async_action(Editor::confirm_completion);
392 cx.add_async_action(Editor::confirm_code_action);
393 cx.add_async_action(Editor::rename);
394 cx.add_async_action(Editor::confirm_rename);
395 cx.add_async_action(Editor::find_all_references);
396 cx.add_action(Editor::next_copilot_suggestion);
397 cx.add_action(Editor::previous_copilot_suggestion);
398
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 RenderContext<Editor>,
725 ) -> (DisplayPoint, ElementBox) {
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 RenderContext<Editor>) -> ElementBox {
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 RenderContext<Editor>,
954 ) -> (DisplayPoint, ElementBox) {
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::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 AppContext) -> 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 .and_then(|this| this.update(&mut cx, |this, _| this.code_actions_task.take()));
2551 }
2552
2553 if let Some(this) = this.upgrade(&cx) {
2554 this.update(&mut cx, |this, cx| {
2555 if this.focused {
2556 if let Some((buffer, actions)) = this.available_code_actions.clone() {
2557 this.show_context_menu(
2558 ContextMenu::CodeActions(CodeActionsMenu {
2559 buffer,
2560 actions,
2561 selected_item: Default::default(),
2562 list: Default::default(),
2563 deployed_from_indicator,
2564 }),
2565 cx,
2566 );
2567 }
2568 }
2569 })
2570 }
2571 Ok::<_, anyhow::Error>(())
2572 })
2573 .detach_and_log_err(cx);
2574 }
2575
2576 pub fn confirm_code_action(
2577 workspace: &mut Workspace,
2578 action: &ConfirmCodeAction,
2579 cx: &mut ViewContext<Workspace>,
2580 ) -> Option<Task<Result<()>>> {
2581 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
2582 let actions_menu = if let ContextMenu::CodeActions(menu) =
2583 editor.update(cx, |editor, cx| editor.hide_context_menu(cx))?
2584 {
2585 menu
2586 } else {
2587 return None;
2588 };
2589 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
2590 let action = actions_menu.actions.get(action_ix)?.clone();
2591 let title = action.lsp_action.title.clone();
2592 let buffer = actions_menu.buffer;
2593
2594 let apply_code_actions = workspace.project().clone().update(cx, |project, cx| {
2595 project.apply_code_action(buffer, action, true, cx)
2596 });
2597 Some(cx.spawn(|workspace, cx| async move {
2598 let project_transaction = apply_code_actions.await?;
2599 Self::open_project_transaction(editor, workspace, project_transaction, title, cx).await
2600 }))
2601 }
2602
2603 async fn open_project_transaction(
2604 this: ViewHandle<Editor>,
2605 workspace: ViewHandle<Workspace>,
2606 transaction: ProjectTransaction,
2607 title: String,
2608 mut cx: AsyncAppContext,
2609 ) -> Result<()> {
2610 let replica_id = this.read_with(&cx, |this, cx| this.replica_id(cx));
2611
2612 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
2613 entries.sort_unstable_by_key(|(buffer, _)| {
2614 buffer.read_with(&cx, |buffer, _| buffer.file().map(|f| f.path().clone()))
2615 });
2616
2617 // If the project transaction's edits are all contained within this editor, then
2618 // avoid opening a new editor to display them.
2619
2620 if let Some((buffer, transaction)) = entries.first() {
2621 if entries.len() == 1 {
2622 let excerpt = this.read_with(&cx, |editor, cx| {
2623 editor
2624 .buffer()
2625 .read(cx)
2626 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
2627 });
2628 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
2629 if excerpted_buffer == *buffer {
2630 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
2631 let excerpt_range = excerpt_range.to_offset(buffer);
2632 buffer
2633 .edited_ranges_for_transaction::<usize>(transaction)
2634 .all(|range| {
2635 excerpt_range.start <= range.start
2636 && excerpt_range.end >= range.end
2637 })
2638 });
2639
2640 if all_edits_within_excerpt {
2641 return Ok(());
2642 }
2643 }
2644 }
2645 }
2646 } else {
2647 return Ok(());
2648 }
2649
2650 let mut ranges_to_highlight = Vec::new();
2651 let excerpt_buffer = cx.add_model(|cx| {
2652 let mut multibuffer = MultiBuffer::new(replica_id).with_title(title);
2653 for (buffer_handle, transaction) in &entries {
2654 let buffer = buffer_handle.read(cx);
2655 ranges_to_highlight.extend(
2656 multibuffer.push_excerpts_with_context_lines(
2657 buffer_handle.clone(),
2658 buffer
2659 .edited_ranges_for_transaction::<usize>(transaction)
2660 .collect(),
2661 1,
2662 cx,
2663 ),
2664 );
2665 }
2666 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)));
2667 multibuffer
2668 });
2669
2670 workspace.update(&mut cx, |workspace, cx| {
2671 let project = workspace.project().clone();
2672 let editor =
2673 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
2674 workspace.add_item(Box::new(editor.clone()), cx);
2675 editor.update(cx, |editor, cx| {
2676 editor.highlight_background::<Self>(
2677 ranges_to_highlight,
2678 |theme| theme.editor.highlighted_line_background,
2679 cx,
2680 );
2681 });
2682 });
2683
2684 Ok(())
2685 }
2686
2687 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
2688 let project = self.project.as_ref()?;
2689 let buffer = self.buffer.read(cx);
2690 let newest_selection = self.selections.newest_anchor().clone();
2691 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
2692 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
2693 if start_buffer != end_buffer {
2694 return None;
2695 }
2696
2697 let actions = project.update(cx, |project, cx| {
2698 project.code_actions(&start_buffer, start..end, cx)
2699 });
2700 self.code_actions_task = Some(cx.spawn_weak(|this, mut cx| async move {
2701 let actions = actions.await;
2702 if let Some(this) = this.upgrade(&cx) {
2703 this.update(&mut cx, |this, cx| {
2704 this.available_code_actions = actions.log_err().and_then(|actions| {
2705 if actions.is_empty() {
2706 None
2707 } else {
2708 Some((start_buffer, actions.into()))
2709 }
2710 });
2711 cx.notify();
2712 })
2713 }
2714 }));
2715 None
2716 }
2717
2718 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
2719 if self.pending_rename.is_some() {
2720 return None;
2721 }
2722
2723 let project = self.project.as_ref()?;
2724 let buffer = self.buffer.read(cx);
2725 let newest_selection = self.selections.newest_anchor().clone();
2726 let cursor_position = newest_selection.head();
2727 let (cursor_buffer, cursor_buffer_position) =
2728 buffer.text_anchor_for_position(cursor_position.clone(), cx)?;
2729 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
2730 if cursor_buffer != tail_buffer {
2731 return None;
2732 }
2733
2734 let highlights = project.update(cx, |project, cx| {
2735 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
2736 });
2737
2738 self.document_highlights_task = Some(cx.spawn_weak(|this, mut cx| async move {
2739 let highlights = highlights.log_err().await;
2740 if let Some((this, highlights)) = this.upgrade(&cx).zip(highlights) {
2741 this.update(&mut cx, |this, cx| {
2742 if this.pending_rename.is_some() {
2743 return;
2744 }
2745
2746 let buffer_id = cursor_position.buffer_id;
2747 let buffer = this.buffer.read(cx);
2748 if !buffer
2749 .text_anchor_for_position(cursor_position, cx)
2750 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
2751 {
2752 return;
2753 }
2754
2755 let cursor_buffer_snapshot = cursor_buffer.read(cx);
2756 let mut write_ranges = Vec::new();
2757 let mut read_ranges = Vec::new();
2758 for highlight in highlights {
2759 for (excerpt_id, excerpt_range) in
2760 buffer.excerpts_for_buffer(&cursor_buffer, cx)
2761 {
2762 let start = highlight
2763 .range
2764 .start
2765 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
2766 let end = highlight
2767 .range
2768 .end
2769 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
2770 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
2771 continue;
2772 }
2773
2774 let range = Anchor {
2775 buffer_id,
2776 excerpt_id: excerpt_id.clone(),
2777 text_anchor: start,
2778 }..Anchor {
2779 buffer_id,
2780 excerpt_id,
2781 text_anchor: end,
2782 };
2783 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
2784 write_ranges.push(range);
2785 } else {
2786 read_ranges.push(range);
2787 }
2788 }
2789 }
2790
2791 this.highlight_background::<DocumentHighlightRead>(
2792 read_ranges,
2793 |theme| theme.editor.document_highlight_read_background,
2794 cx,
2795 );
2796 this.highlight_background::<DocumentHighlightWrite>(
2797 write_ranges,
2798 |theme| theme.editor.document_highlight_write_background,
2799 cx,
2800 );
2801 cx.notify();
2802 });
2803 }
2804 }));
2805 None
2806 }
2807
2808 fn refresh_copilot_suggestions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
2809 let copilot = Copilot::global(cx)?;
2810 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
2811 self.clear_copilot_suggestions(cx);
2812 return None;
2813 }
2814 self.update_visible_copilot_suggestion(cx);
2815
2816 let snapshot = self.buffer.read(cx).snapshot(cx);
2817 let cursor = self.selections.newest_anchor().head();
2818 let language_name = snapshot.language_at(cursor).map(|language| language.name());
2819 if !cx
2820 .global::<Settings>()
2821 .show_copilot_suggestions(language_name.as_deref())
2822 {
2823 self.clear_copilot_suggestions(cx);
2824 return None;
2825 }
2826
2827 let (buffer, buffer_position) =
2828 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
2829 self.copilot_state.pending_refresh = cx.spawn_weak(|this, mut cx| async move {
2830 cx.background().timer(COPILOT_DEBOUNCE_TIMEOUT).await;
2831 let (completion, completions_cycling) = copilot.update(&mut cx, |copilot, cx| {
2832 (
2833 copilot.completions(&buffer, buffer_position, cx),
2834 copilot.completions_cycling(&buffer, buffer_position, cx),
2835 )
2836 });
2837
2838 let (completion, completions_cycling) = futures::join!(completion, completions_cycling);
2839 let mut completions = Vec::new();
2840 completions.extend(completion.log_err().into_iter().flatten());
2841 completions.extend(completions_cycling.log_err().into_iter().flatten());
2842 this.upgrade(&cx)?.update(&mut cx, |this, cx| {
2843 if !completions.is_empty() {
2844 this.copilot_state.completions.clear();
2845 this.copilot_state.active_completion_index = 0;
2846 this.copilot_state.excerpt_id = Some(cursor.excerpt_id);
2847 for completion in completions {
2848 this.copilot_state.push_completion(completion);
2849 }
2850 this.update_visible_copilot_suggestion(cx);
2851 }
2852 });
2853
2854 Some(())
2855 });
2856
2857 Some(())
2858 }
2859
2860 fn next_copilot_suggestion(&mut self, _: &copilot::NextSuggestion, cx: &mut ViewContext<Self>) {
2861 if !self.has_active_copilot_suggestion(cx) {
2862 self.refresh_copilot_suggestions(cx);
2863 return;
2864 }
2865
2866 self.copilot_state.active_completion_index =
2867 (self.copilot_state.active_completion_index + 1) % self.copilot_state.completions.len();
2868 self.update_visible_copilot_suggestion(cx);
2869 }
2870
2871 fn previous_copilot_suggestion(
2872 &mut self,
2873 _: &copilot::PreviousSuggestion,
2874 cx: &mut ViewContext<Self>,
2875 ) {
2876 if !self.has_active_copilot_suggestion(cx) {
2877 self.refresh_copilot_suggestions(cx);
2878 return;
2879 }
2880
2881 self.copilot_state.active_completion_index =
2882 if self.copilot_state.active_completion_index == 0 {
2883 self.copilot_state.completions.len() - 1
2884 } else {
2885 self.copilot_state.active_completion_index - 1
2886 };
2887 self.update_visible_copilot_suggestion(cx);
2888 }
2889
2890 fn accept_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
2891 if let Some(text) = self.hide_copilot_suggestion(cx) {
2892 self.insert_with_autoindent_mode(&text.to_string(), None, cx);
2893 true
2894 } else {
2895 false
2896 }
2897 }
2898
2899 fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool {
2900 self.display_map.read(cx).has_suggestion()
2901 }
2902
2903 fn hide_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> Option<Rope> {
2904 if self.has_active_copilot_suggestion(cx) {
2905 let old_suggestion = self
2906 .display_map
2907 .update(cx, |map, cx| map.replace_suggestion::<usize>(None, cx));
2908 cx.notify();
2909 old_suggestion.map(|suggestion| suggestion.text)
2910 } else {
2911 None
2912 }
2913 }
2914
2915 fn update_visible_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) {
2916 let snapshot = self.buffer.read(cx).snapshot(cx);
2917 let selection = self.selections.newest_anchor();
2918 let cursor = selection.head();
2919
2920 if self.context_menu.is_some()
2921 || !self.completion_tasks.is_empty()
2922 || selection.start != selection.end
2923 {
2924 self.hide_copilot_suggestion(cx);
2925 } else if let Some(text) = self
2926 .copilot_state
2927 .text_for_active_completion(cursor, &snapshot)
2928 {
2929 self.display_map.update(cx, |map, cx| {
2930 map.replace_suggestion(
2931 Some(Suggestion {
2932 position: cursor,
2933 text: text.into(),
2934 }),
2935 cx,
2936 )
2937 });
2938 cx.notify();
2939 } else {
2940 self.hide_copilot_suggestion(cx);
2941 }
2942 }
2943
2944 fn clear_copilot_suggestions(&mut self, cx: &mut ViewContext<Self>) {
2945 self.copilot_state = Default::default();
2946 self.hide_copilot_suggestion(cx);
2947 }
2948
2949 pub fn render_code_actions_indicator(
2950 &self,
2951 style: &EditorStyle,
2952 active: bool,
2953 cx: &mut RenderContext<Self>,
2954 ) -> Option<ElementBox> {
2955 if self.available_code_actions.is_some() {
2956 enum CodeActions {}
2957 Some(
2958 MouseEventHandler::<CodeActions>::new(0, cx, |state, _| {
2959 Svg::new("icons/bolt_8.svg")
2960 .with_color(style.code_actions.indicator.style_for(state, active).color)
2961 .boxed()
2962 })
2963 .with_cursor_style(CursorStyle::PointingHand)
2964 .with_padding(Padding::uniform(3.))
2965 .on_down(MouseButton::Left, |_, cx| {
2966 cx.dispatch_action(ToggleCodeActions {
2967 deployed_from_indicator: true,
2968 });
2969 })
2970 .boxed(),
2971 )
2972 } else {
2973 None
2974 }
2975 }
2976
2977 pub fn render_fold_indicators(
2978 &self,
2979 fold_data: Vec<Option<(FoldStatus, u32, bool)>>,
2980 style: &EditorStyle,
2981 gutter_hovered: bool,
2982 line_height: f32,
2983 gutter_margin: f32,
2984 cx: &mut RenderContext<Self>,
2985 ) -> Vec<Option<ElementBox>> {
2986 enum FoldIndicators {}
2987
2988 let style = style.folds.clone();
2989
2990 fold_data
2991 .iter()
2992 .enumerate()
2993 .map(|(ix, fold_data)| {
2994 fold_data
2995 .map(|(fold_status, buffer_row, active)| {
2996 (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| {
2997 MouseEventHandler::<FoldIndicators>::new(
2998 ix as usize,
2999 cx,
3000 |mouse_state, _| -> ElementBox {
3001 Svg::new(match fold_status {
3002 FoldStatus::Folded => style.folded_icon.clone(),
3003 FoldStatus::Foldable => style.foldable_icon.clone(),
3004 })
3005 .with_color(
3006 style
3007 .indicator
3008 .style_for(
3009 mouse_state,
3010 fold_status == FoldStatus::Folded,
3011 )
3012 .color,
3013 )
3014 .constrained()
3015 .with_width(gutter_margin * style.icon_margin_scale)
3016 .aligned()
3017 .constrained()
3018 .with_height(line_height)
3019 .with_width(gutter_margin)
3020 .aligned()
3021 .boxed()
3022 },
3023 )
3024 .with_cursor_style(CursorStyle::PointingHand)
3025 .with_padding(Padding::uniform(3.))
3026 .on_click(MouseButton::Left, {
3027 move |_, cx| {
3028 cx.dispatch_any_action(match fold_status {
3029 FoldStatus::Folded => Box::new(UnfoldAt { buffer_row }),
3030 FoldStatus::Foldable => Box::new(FoldAt { buffer_row }),
3031 });
3032 }
3033 })
3034 .boxed()
3035 })
3036 })
3037 .flatten()
3038 })
3039 .collect()
3040 }
3041
3042 pub fn context_menu_visible(&self) -> bool {
3043 self.context_menu
3044 .as_ref()
3045 .map_or(false, |menu| menu.visible())
3046 }
3047
3048 pub fn render_context_menu(
3049 &self,
3050 cursor_position: DisplayPoint,
3051 style: EditorStyle,
3052 cx: &mut RenderContext<Editor>,
3053 ) -> Option<(DisplayPoint, ElementBox)> {
3054 self.context_menu
3055 .as_ref()
3056 .map(|menu| menu.render(cursor_position, style, cx))
3057 }
3058
3059 fn show_context_menu(&mut self, menu: ContextMenu, cx: &mut ViewContext<Self>) {
3060 if !matches!(menu, ContextMenu::Completions(_)) {
3061 self.completion_tasks.clear();
3062 }
3063 self.context_menu = Some(menu);
3064 self.hide_copilot_suggestion(cx);
3065 cx.notify();
3066 }
3067
3068 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
3069 cx.notify();
3070 self.completion_tasks.clear();
3071 let context_menu = self.context_menu.take();
3072 if context_menu.is_some() {
3073 self.update_visible_copilot_suggestion(cx);
3074 }
3075 context_menu
3076 }
3077
3078 pub fn insert_snippet(
3079 &mut self,
3080 insertion_ranges: &[Range<usize>],
3081 snippet: Snippet,
3082 cx: &mut ViewContext<Self>,
3083 ) -> Result<()> {
3084 let tabstops = self.buffer.update(cx, |buffer, cx| {
3085 let snippet_text: Arc<str> = snippet.text.clone().into();
3086 buffer.edit(
3087 insertion_ranges
3088 .iter()
3089 .cloned()
3090 .map(|range| (range, snippet_text.clone())),
3091 Some(AutoindentMode::EachLine),
3092 cx,
3093 );
3094
3095 let snapshot = &*buffer.read(cx);
3096 let snippet = &snippet;
3097 snippet
3098 .tabstops
3099 .iter()
3100 .map(|tabstop| {
3101 let mut tabstop_ranges = tabstop
3102 .iter()
3103 .flat_map(|tabstop_range| {
3104 let mut delta = 0_isize;
3105 insertion_ranges.iter().map(move |insertion_range| {
3106 let insertion_start = insertion_range.start as isize + delta;
3107 delta +=
3108 snippet.text.len() as isize - insertion_range.len() as isize;
3109
3110 let start = snapshot.anchor_before(
3111 (insertion_start + tabstop_range.start) as usize,
3112 );
3113 let end = snapshot
3114 .anchor_after((insertion_start + tabstop_range.end) as usize);
3115 start..end
3116 })
3117 })
3118 .collect::<Vec<_>>();
3119 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
3120 tabstop_ranges
3121 })
3122 .collect::<Vec<_>>()
3123 });
3124
3125 if let Some(tabstop) = tabstops.first() {
3126 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
3127 s.select_ranges(tabstop.iter().cloned());
3128 });
3129 self.snippet_stack.push(SnippetState {
3130 active_index: 0,
3131 ranges: tabstops,
3132 });
3133 }
3134
3135 Ok(())
3136 }
3137
3138 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
3139 self.move_to_snippet_tabstop(Bias::Right, cx)
3140 }
3141
3142 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
3143 self.move_to_snippet_tabstop(Bias::Left, cx)
3144 }
3145
3146 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
3147 if let Some(mut snippet) = self.snippet_stack.pop() {
3148 match bias {
3149 Bias::Left => {
3150 if snippet.active_index > 0 {
3151 snippet.active_index -= 1;
3152 } else {
3153 self.snippet_stack.push(snippet);
3154 return false;
3155 }
3156 }
3157 Bias::Right => {
3158 if snippet.active_index + 1 < snippet.ranges.len() {
3159 snippet.active_index += 1;
3160 } else {
3161 self.snippet_stack.push(snippet);
3162 return false;
3163 }
3164 }
3165 }
3166 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
3167 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
3168 s.select_anchor_ranges(current_ranges.iter().cloned())
3169 });
3170 // If snippet state is not at the last tabstop, push it back on the stack
3171 if snippet.active_index + 1 < snippet.ranges.len() {
3172 self.snippet_stack.push(snippet);
3173 }
3174 return true;
3175 }
3176 }
3177
3178 false
3179 }
3180
3181 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
3182 self.transact(cx, |this, cx| {
3183 this.select_all(&SelectAll, cx);
3184 this.insert("", cx);
3185 });
3186 }
3187
3188 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
3189 self.transact(cx, |this, cx| {
3190 this.select_autoclose_pair(cx);
3191 let mut selections = this.selections.all::<Point>(cx);
3192 if !this.selections.line_mode {
3193 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
3194 for selection in &mut selections {
3195 if selection.is_empty() {
3196 let old_head = selection.head();
3197 let mut new_head =
3198 movement::left(&display_map, old_head.to_display_point(&display_map))
3199 .to_point(&display_map);
3200 if let Some((buffer, line_buffer_range)) = display_map
3201 .buffer_snapshot
3202 .buffer_line_for_row(old_head.row)
3203 {
3204 let indent_size =
3205 buffer.indent_size_for_line(line_buffer_range.start.row);
3206 let language_name = buffer
3207 .language_at(line_buffer_range.start)
3208 .map(|language| language.name());
3209 let indent_len = match indent_size.kind {
3210 IndentKind::Space => {
3211 cx.global::<Settings>().tab_size(language_name.as_deref())
3212 }
3213 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
3214 };
3215 if old_head.column <= indent_size.len && old_head.column > 0 {
3216 let indent_len = indent_len.get();
3217 new_head = cmp::min(
3218 new_head,
3219 Point::new(
3220 old_head.row,
3221 ((old_head.column - 1) / indent_len) * indent_len,
3222 ),
3223 );
3224 }
3225 }
3226
3227 selection.set_head(new_head, SelectionGoal::None);
3228 }
3229 }
3230 }
3231
3232 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3233 this.insert("", cx);
3234 this.refresh_copilot_suggestions(cx);
3235 });
3236 }
3237
3238 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
3239 self.transact(cx, |this, cx| {
3240 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3241 let line_mode = s.line_mode;
3242 s.move_with(|map, selection| {
3243 if selection.is_empty() && !line_mode {
3244 let cursor = movement::right(map, selection.head());
3245 selection.set_head(cursor, SelectionGoal::None);
3246 }
3247 })
3248 });
3249 this.insert("", cx);
3250 this.refresh_copilot_suggestions(cx);
3251 });
3252 }
3253
3254 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
3255 if self.move_to_prev_snippet_tabstop(cx) {
3256 return;
3257 }
3258
3259 self.outdent(&Outdent, cx);
3260 }
3261
3262 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
3263 if self.move_to_next_snippet_tabstop(cx) {
3264 return;
3265 }
3266
3267 let mut selections = self.selections.all_adjusted(cx);
3268 let buffer = self.buffer.read(cx);
3269 let snapshot = buffer.snapshot(cx);
3270 let rows_iter = selections.iter().map(|s| s.head().row);
3271 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
3272
3273 let mut edits = Vec::new();
3274 let mut prev_edited_row = 0;
3275 let mut row_delta = 0;
3276 for selection in &mut selections {
3277 if selection.start.row != prev_edited_row {
3278 row_delta = 0;
3279 }
3280 prev_edited_row = selection.end.row;
3281
3282 // If the selection is non-empty, then increase the indentation of the selected lines.
3283 if !selection.is_empty() {
3284 row_delta =
3285 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3286 continue;
3287 }
3288
3289 // If the selection is empty and the cursor is in the leading whitespace before the
3290 // suggested indentation, then auto-indent the line.
3291 let cursor = selection.head();
3292 let current_indent = snapshot.indent_size_for_line(cursor.row);
3293 if let Some(suggested_indent) = suggested_indents.get(&cursor.row).copied() {
3294 if cursor.column < suggested_indent.len
3295 && cursor.column <= current_indent.len
3296 && current_indent.len <= suggested_indent.len
3297 {
3298 selection.start = Point::new(cursor.row, suggested_indent.len);
3299 selection.end = selection.start;
3300 if row_delta == 0 {
3301 edits.extend(Buffer::edit_for_indent_size_adjustment(
3302 cursor.row,
3303 current_indent,
3304 suggested_indent,
3305 ));
3306 row_delta = suggested_indent.len - current_indent.len;
3307 }
3308 continue;
3309 }
3310 }
3311
3312 // Accept copilot suggestion if there is only one selection and the cursor is not
3313 // in the leading whitespace.
3314 if self.selections.count() == 1
3315 && cursor.column >= current_indent.len
3316 && self.has_active_copilot_suggestion(cx)
3317 {
3318 self.accept_copilot_suggestion(cx);
3319 return;
3320 }
3321
3322 // Otherwise, insert a hard or soft tab.
3323 let settings = cx.global::<Settings>();
3324 let language_name = buffer.language_at(cursor, cx).map(|l| l.name());
3325 let tab_size = if settings.hard_tabs(language_name.as_deref()) {
3326 IndentSize::tab()
3327 } else {
3328 let tab_size = settings.tab_size(language_name.as_deref()).get();
3329 let char_column = snapshot
3330 .text_for_range(Point::new(cursor.row, 0)..cursor)
3331 .flat_map(str::chars)
3332 .count()
3333 + row_delta as usize;
3334 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
3335 IndentSize::spaces(chars_to_next_tab_stop)
3336 };
3337 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
3338 selection.end = selection.start;
3339 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
3340 row_delta += tab_size.len;
3341 }
3342
3343 self.transact(cx, |this, cx| {
3344 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3345 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3346 this.refresh_copilot_suggestions(cx);
3347 });
3348 }
3349
3350 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
3351 let mut selections = self.selections.all::<Point>(cx);
3352 let mut prev_edited_row = 0;
3353 let mut row_delta = 0;
3354 let mut edits = Vec::new();
3355 let buffer = self.buffer.read(cx);
3356 let snapshot = buffer.snapshot(cx);
3357 for selection in &mut selections {
3358 if selection.start.row != prev_edited_row {
3359 row_delta = 0;
3360 }
3361 prev_edited_row = selection.end.row;
3362
3363 row_delta =
3364 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3365 }
3366
3367 self.transact(cx, |this, cx| {
3368 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3369 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3370 });
3371 }
3372
3373 fn indent_selection(
3374 buffer: &MultiBuffer,
3375 snapshot: &MultiBufferSnapshot,
3376 selection: &mut Selection<Point>,
3377 edits: &mut Vec<(Range<Point>, String)>,
3378 delta_for_start_row: u32,
3379 cx: &AppContext,
3380 ) -> u32 {
3381 let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
3382 let settings = cx.global::<Settings>();
3383 let tab_size = settings.tab_size(language_name.as_deref()).get();
3384 let indent_kind = if settings.hard_tabs(language_name.as_deref()) {
3385 IndentKind::Tab
3386 } else {
3387 IndentKind::Space
3388 };
3389 let mut start_row = selection.start.row;
3390 let mut end_row = selection.end.row + 1;
3391
3392 // If a selection ends at the beginning of a line, don't indent
3393 // that last line.
3394 if selection.end.column == 0 {
3395 end_row -= 1;
3396 }
3397
3398 // Avoid re-indenting a row that has already been indented by a
3399 // previous selection, but still update this selection's column
3400 // to reflect that indentation.
3401 if delta_for_start_row > 0 {
3402 start_row += 1;
3403 selection.start.column += delta_for_start_row;
3404 if selection.end.row == selection.start.row {
3405 selection.end.column += delta_for_start_row;
3406 }
3407 }
3408
3409 let mut delta_for_end_row = 0;
3410 for row in start_row..end_row {
3411 let current_indent = snapshot.indent_size_for_line(row);
3412 let indent_delta = match (current_indent.kind, indent_kind) {
3413 (IndentKind::Space, IndentKind::Space) => {
3414 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
3415 IndentSize::spaces(columns_to_next_tab_stop)
3416 }
3417 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
3418 (_, IndentKind::Tab) => IndentSize::tab(),
3419 };
3420
3421 let row_start = Point::new(row, 0);
3422 edits.push((
3423 row_start..row_start,
3424 indent_delta.chars().collect::<String>(),
3425 ));
3426
3427 // Update this selection's endpoints to reflect the indentation.
3428 if row == selection.start.row {
3429 selection.start.column += indent_delta.len;
3430 }
3431 if row == selection.end.row {
3432 selection.end.column += indent_delta.len;
3433 delta_for_end_row = indent_delta.len;
3434 }
3435 }
3436
3437 if selection.start.row == selection.end.row {
3438 delta_for_start_row + delta_for_end_row
3439 } else {
3440 delta_for_end_row
3441 }
3442 }
3443
3444 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
3445 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3446 let selections = self.selections.all::<Point>(cx);
3447 let mut deletion_ranges = Vec::new();
3448 let mut last_outdent = None;
3449 {
3450 let buffer = self.buffer.read(cx);
3451 let snapshot = buffer.snapshot(cx);
3452 for selection in &selections {
3453 let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
3454 let tab_size = cx
3455 .global::<Settings>()
3456 .tab_size(language_name.as_deref())
3457 .get();
3458 let mut rows = selection.spanned_rows(false, &display_map);
3459
3460 // Avoid re-outdenting a row that has already been outdented by a
3461 // previous selection.
3462 if let Some(last_row) = last_outdent {
3463 if last_row == rows.start {
3464 rows.start += 1;
3465 }
3466 }
3467
3468 for row in rows {
3469 let indent_size = snapshot.indent_size_for_line(row);
3470 if indent_size.len > 0 {
3471 let deletion_len = match indent_size.kind {
3472 IndentKind::Space => {
3473 let columns_to_prev_tab_stop = indent_size.len % tab_size;
3474 if columns_to_prev_tab_stop == 0 {
3475 tab_size
3476 } else {
3477 columns_to_prev_tab_stop
3478 }
3479 }
3480 IndentKind::Tab => 1,
3481 };
3482 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
3483 last_outdent = Some(row);
3484 }
3485 }
3486 }
3487 }
3488
3489 self.transact(cx, |this, cx| {
3490 this.buffer.update(cx, |buffer, cx| {
3491 let empty_str: Arc<str> = "".into();
3492 buffer.edit(
3493 deletion_ranges
3494 .into_iter()
3495 .map(|range| (range, empty_str.clone())),
3496 None,
3497 cx,
3498 );
3499 });
3500 let selections = this.selections.all::<usize>(cx);
3501 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3502 });
3503 }
3504
3505 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
3506 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3507 let selections = self.selections.all::<Point>(cx);
3508
3509 let mut new_cursors = Vec::new();
3510 let mut edit_ranges = Vec::new();
3511 let mut selections = selections.iter().peekable();
3512 while let Some(selection) = selections.next() {
3513 let mut rows = selection.spanned_rows(false, &display_map);
3514 let goal_display_column = selection.head().to_display_point(&display_map).column();
3515
3516 // Accumulate contiguous regions of rows that we want to delete.
3517 while let Some(next_selection) = selections.peek() {
3518 let next_rows = next_selection.spanned_rows(false, &display_map);
3519 if next_rows.start <= rows.end {
3520 rows.end = next_rows.end;
3521 selections.next().unwrap();
3522 } else {
3523 break;
3524 }
3525 }
3526
3527 let buffer = &display_map.buffer_snapshot;
3528 let mut edit_start = Point::new(rows.start, 0).to_offset(buffer);
3529 let edit_end;
3530 let cursor_buffer_row;
3531 if buffer.max_point().row >= rows.end {
3532 // If there's a line after the range, delete the \n from the end of the row range
3533 // and position the cursor on the next line.
3534 edit_end = Point::new(rows.end, 0).to_offset(buffer);
3535 cursor_buffer_row = rows.end;
3536 } else {
3537 // If there isn't a line after the range, delete the \n from the line before the
3538 // start of the row range and position the cursor there.
3539 edit_start = edit_start.saturating_sub(1);
3540 edit_end = buffer.len();
3541 cursor_buffer_row = rows.start.saturating_sub(1);
3542 }
3543
3544 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
3545 *cursor.column_mut() =
3546 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
3547
3548 new_cursors.push((
3549 selection.id,
3550 buffer.anchor_after(cursor.to_point(&display_map)),
3551 ));
3552 edit_ranges.push(edit_start..edit_end);
3553 }
3554
3555 self.transact(cx, |this, cx| {
3556 let buffer = this.buffer.update(cx, |buffer, cx| {
3557 let empty_str: Arc<str> = "".into();
3558 buffer.edit(
3559 edit_ranges
3560 .into_iter()
3561 .map(|range| (range, empty_str.clone())),
3562 None,
3563 cx,
3564 );
3565 buffer.snapshot(cx)
3566 });
3567 let new_selections = new_cursors
3568 .into_iter()
3569 .map(|(id, cursor)| {
3570 let cursor = cursor.to_point(&buffer);
3571 Selection {
3572 id,
3573 start: cursor,
3574 end: cursor,
3575 reversed: false,
3576 goal: SelectionGoal::None,
3577 }
3578 })
3579 .collect();
3580
3581 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3582 s.select(new_selections);
3583 });
3584 });
3585 }
3586
3587 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
3588 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3589 let buffer = &display_map.buffer_snapshot;
3590 let selections = self.selections.all::<Point>(cx);
3591
3592 let mut edits = Vec::new();
3593 let mut selections_iter = selections.iter().peekable();
3594 while let Some(selection) = selections_iter.next() {
3595 // Avoid duplicating the same lines twice.
3596 let mut rows = selection.spanned_rows(false, &display_map);
3597
3598 while let Some(next_selection) = selections_iter.peek() {
3599 let next_rows = next_selection.spanned_rows(false, &display_map);
3600 if next_rows.start < rows.end {
3601 rows.end = next_rows.end;
3602 selections_iter.next().unwrap();
3603 } else {
3604 break;
3605 }
3606 }
3607
3608 // Copy the text from the selected row region and splice it at the start of the region.
3609 let start = Point::new(rows.start, 0);
3610 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
3611 let text = buffer
3612 .text_for_range(start..end)
3613 .chain(Some("\n"))
3614 .collect::<String>();
3615 edits.push((start..start, text));
3616 }
3617
3618 self.transact(cx, |this, cx| {
3619 this.buffer.update(cx, |buffer, cx| {
3620 buffer.edit(edits, None, cx);
3621 });
3622
3623 this.request_autoscroll(Autoscroll::fit(), cx);
3624 });
3625 }
3626
3627 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
3628 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3629 let buffer = self.buffer.read(cx).snapshot(cx);
3630
3631 let mut edits = Vec::new();
3632 let mut unfold_ranges = Vec::new();
3633 let mut refold_ranges = Vec::new();
3634
3635 let selections = self.selections.all::<Point>(cx);
3636 let mut selections = selections.iter().peekable();
3637 let mut contiguous_row_selections = Vec::new();
3638 let mut new_selections = Vec::new();
3639
3640 while let Some(selection) = selections.next() {
3641 // Find all the selections that span a contiguous row range
3642 let (start_row, end_row) = consume_contiguous_rows(
3643 &mut contiguous_row_selections,
3644 selection,
3645 &display_map,
3646 &mut selections,
3647 );
3648
3649 // Move the text spanned by the row range to be before the line preceding the row range
3650 if start_row > 0 {
3651 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
3652 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
3653 let insertion_point = display_map
3654 .prev_line_boundary(Point::new(start_row - 1, 0))
3655 .0;
3656
3657 // Don't move lines across excerpts
3658 if buffer
3659 .excerpt_boundaries_in_range((
3660 Bound::Excluded(insertion_point),
3661 Bound::Included(range_to_move.end),
3662 ))
3663 .next()
3664 .is_none()
3665 {
3666 let text = buffer
3667 .text_for_range(range_to_move.clone())
3668 .flat_map(|s| s.chars())
3669 .skip(1)
3670 .chain(['\n'])
3671 .collect::<String>();
3672
3673 edits.push((
3674 buffer.anchor_after(range_to_move.start)
3675 ..buffer.anchor_before(range_to_move.end),
3676 String::new(),
3677 ));
3678 let insertion_anchor = buffer.anchor_after(insertion_point);
3679 edits.push((insertion_anchor..insertion_anchor, text));
3680
3681 let row_delta = range_to_move.start.row - insertion_point.row + 1;
3682
3683 // Move selections up
3684 new_selections.extend(contiguous_row_selections.drain(..).map(
3685 |mut selection| {
3686 selection.start.row -= row_delta;
3687 selection.end.row -= row_delta;
3688 selection
3689 },
3690 ));
3691
3692 // Move folds up
3693 unfold_ranges.push(range_to_move.clone());
3694 for fold in display_map.folds_in_range(
3695 buffer.anchor_before(range_to_move.start)
3696 ..buffer.anchor_after(range_to_move.end),
3697 ) {
3698 let mut start = fold.start.to_point(&buffer);
3699 let mut end = fold.end.to_point(&buffer);
3700 start.row -= row_delta;
3701 end.row -= row_delta;
3702 refold_ranges.push(start..end);
3703 }
3704 }
3705 }
3706
3707 // If we didn't move line(s), preserve the existing selections
3708 new_selections.append(&mut contiguous_row_selections);
3709 }
3710
3711 self.transact(cx, |this, cx| {
3712 this.unfold_ranges(unfold_ranges, true, true, cx);
3713 this.buffer.update(cx, |buffer, cx| {
3714 for (range, text) in edits {
3715 buffer.edit([(range, text)], None, cx);
3716 }
3717 });
3718 this.fold_ranges(refold_ranges, true, cx);
3719 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3720 s.select(new_selections);
3721 })
3722 });
3723 }
3724
3725 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
3726 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3727 let buffer = self.buffer.read(cx).snapshot(cx);
3728
3729 let mut edits = Vec::new();
3730 let mut unfold_ranges = Vec::new();
3731 let mut refold_ranges = Vec::new();
3732
3733 let selections = self.selections.all::<Point>(cx);
3734 let mut selections = selections.iter().peekable();
3735 let mut contiguous_row_selections = Vec::new();
3736 let mut new_selections = Vec::new();
3737
3738 while let Some(selection) = selections.next() {
3739 // Find all the selections that span a contiguous row range
3740 let (start_row, end_row) = consume_contiguous_rows(
3741 &mut contiguous_row_selections,
3742 selection,
3743 &display_map,
3744 &mut selections,
3745 );
3746
3747 // Move the text spanned by the row range to be after the last line of the row range
3748 if end_row <= buffer.max_point().row {
3749 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
3750 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
3751
3752 // Don't move lines across excerpt boundaries
3753 if buffer
3754 .excerpt_boundaries_in_range((
3755 Bound::Excluded(range_to_move.start),
3756 Bound::Included(insertion_point),
3757 ))
3758 .next()
3759 .is_none()
3760 {
3761 let mut text = String::from("\n");
3762 text.extend(buffer.text_for_range(range_to_move.clone()));
3763 text.pop(); // Drop trailing newline
3764 edits.push((
3765 buffer.anchor_after(range_to_move.start)
3766 ..buffer.anchor_before(range_to_move.end),
3767 String::new(),
3768 ));
3769 let insertion_anchor = buffer.anchor_after(insertion_point);
3770 edits.push((insertion_anchor..insertion_anchor, text));
3771
3772 let row_delta = insertion_point.row - range_to_move.end.row + 1;
3773
3774 // Move selections down
3775 new_selections.extend(contiguous_row_selections.drain(..).map(
3776 |mut selection| {
3777 selection.start.row += row_delta;
3778 selection.end.row += row_delta;
3779 selection
3780 },
3781 ));
3782
3783 // Move folds down
3784 unfold_ranges.push(range_to_move.clone());
3785 for fold in display_map.folds_in_range(
3786 buffer.anchor_before(range_to_move.start)
3787 ..buffer.anchor_after(range_to_move.end),
3788 ) {
3789 let mut start = fold.start.to_point(&buffer);
3790 let mut end = fold.end.to_point(&buffer);
3791 start.row += row_delta;
3792 end.row += row_delta;
3793 refold_ranges.push(start..end);
3794 }
3795 }
3796 }
3797
3798 // If we didn't move line(s), preserve the existing selections
3799 new_selections.append(&mut contiguous_row_selections);
3800 }
3801
3802 self.transact(cx, |this, cx| {
3803 this.unfold_ranges(unfold_ranges, true, true, cx);
3804 this.buffer.update(cx, |buffer, cx| {
3805 for (range, text) in edits {
3806 buffer.edit([(range, text)], None, cx);
3807 }
3808 });
3809 this.fold_ranges(refold_ranges, true, cx);
3810 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
3811 });
3812 }
3813
3814 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
3815 self.transact(cx, |this, cx| {
3816 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3817 let mut edits: Vec<(Range<usize>, String)> = Default::default();
3818 let line_mode = s.line_mode;
3819 s.move_with(|display_map, selection| {
3820 if !selection.is_empty() || line_mode {
3821 return;
3822 }
3823
3824 let mut head = selection.head();
3825 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
3826 if head.column() == display_map.line_len(head.row()) {
3827 transpose_offset = display_map
3828 .buffer_snapshot
3829 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
3830 }
3831
3832 if transpose_offset == 0 {
3833 return;
3834 }
3835
3836 *head.column_mut() += 1;
3837 head = display_map.clip_point(head, Bias::Right);
3838 selection.collapse_to(head, SelectionGoal::Column(head.column()));
3839
3840 let transpose_start = display_map
3841 .buffer_snapshot
3842 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
3843 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
3844 let transpose_end = display_map
3845 .buffer_snapshot
3846 .clip_offset(transpose_offset + 1, Bias::Right);
3847 if let Some(ch) =
3848 display_map.buffer_snapshot.chars_at(transpose_start).next()
3849 {
3850 edits.push((transpose_start..transpose_offset, String::new()));
3851 edits.push((transpose_end..transpose_end, ch.to_string()));
3852 }
3853 }
3854 });
3855 edits
3856 });
3857 this.buffer
3858 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
3859 let selections = this.selections.all::<usize>(cx);
3860 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3861 s.select(selections);
3862 });
3863 });
3864 }
3865
3866 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
3867 let mut text = String::new();
3868 let buffer = self.buffer.read(cx).snapshot(cx);
3869 let mut selections = self.selections.all::<Point>(cx);
3870 let mut clipboard_selections = Vec::with_capacity(selections.len());
3871 {
3872 let max_point = buffer.max_point();
3873 for selection in &mut selections {
3874 let is_entire_line = selection.is_empty() || self.selections.line_mode;
3875 if is_entire_line {
3876 selection.start = Point::new(selection.start.row, 0);
3877 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
3878 selection.goal = SelectionGoal::None;
3879 }
3880 let mut len = 0;
3881 for chunk in buffer.text_for_range(selection.start..selection.end) {
3882 text.push_str(chunk);
3883 len += chunk.len();
3884 }
3885 clipboard_selections.push(ClipboardSelection {
3886 len,
3887 is_entire_line,
3888 first_line_indent: buffer.indent_size_for_line(selection.start.row).len,
3889 });
3890 }
3891 }
3892
3893 self.transact(cx, |this, cx| {
3894 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3895 s.select(selections);
3896 });
3897 this.insert("", cx);
3898 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
3899 });
3900 }
3901
3902 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
3903 let selections = self.selections.all::<Point>(cx);
3904 let buffer = self.buffer.read(cx).read(cx);
3905 let mut text = String::new();
3906
3907 let mut clipboard_selections = Vec::with_capacity(selections.len());
3908 {
3909 let max_point = buffer.max_point();
3910 for selection in selections.iter() {
3911 let mut start = selection.start;
3912 let mut end = selection.end;
3913 let is_entire_line = selection.is_empty() || self.selections.line_mode;
3914 if is_entire_line {
3915 start = Point::new(start.row, 0);
3916 end = cmp::min(max_point, Point::new(end.row + 1, 0));
3917 }
3918 let mut len = 0;
3919 for chunk in buffer.text_for_range(start..end) {
3920 text.push_str(chunk);
3921 len += chunk.len();
3922 }
3923 clipboard_selections.push(ClipboardSelection {
3924 len,
3925 is_entire_line,
3926 first_line_indent: buffer.indent_size_for_line(start.row).len,
3927 });
3928 }
3929 }
3930
3931 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
3932 }
3933
3934 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
3935 self.transact(cx, |this, cx| {
3936 if let Some(item) = cx.as_mut().read_from_clipboard() {
3937 let mut clipboard_text = Cow::Borrowed(item.text());
3938 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
3939 let old_selections = this.selections.all::<usize>(cx);
3940 let all_selections_were_entire_line =
3941 clipboard_selections.iter().all(|s| s.is_entire_line);
3942 let first_selection_indent_column =
3943 clipboard_selections.first().map(|s| s.first_line_indent);
3944 if clipboard_selections.len() != old_selections.len() {
3945 let mut newline_separated_text = String::new();
3946 let mut clipboard_selections = clipboard_selections.drain(..).peekable();
3947 let mut ix = 0;
3948 while let Some(clipboard_selection) = clipboard_selections.next() {
3949 newline_separated_text
3950 .push_str(&clipboard_text[ix..ix + clipboard_selection.len]);
3951 ix += clipboard_selection.len;
3952 if clipboard_selections.peek().is_some() {
3953 newline_separated_text.push('\n');
3954 }
3955 }
3956 clipboard_text = Cow::Owned(newline_separated_text);
3957 }
3958
3959 this.buffer.update(cx, |buffer, cx| {
3960 let snapshot = buffer.read(cx);
3961 let mut start_offset = 0;
3962 let mut edits = Vec::new();
3963 let mut original_indent_columns = Vec::new();
3964 let line_mode = this.selections.line_mode;
3965 for (ix, selection) in old_selections.iter().enumerate() {
3966 let to_insert;
3967 let entire_line;
3968 let original_indent_column;
3969 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
3970 let end_offset = start_offset + clipboard_selection.len;
3971 to_insert = &clipboard_text[start_offset..end_offset];
3972 entire_line = clipboard_selection.is_entire_line;
3973 start_offset = end_offset;
3974 original_indent_column =
3975 Some(clipboard_selection.first_line_indent);
3976 } else {
3977 to_insert = clipboard_text.as_str();
3978 entire_line = all_selections_were_entire_line;
3979 original_indent_column = first_selection_indent_column
3980 }
3981
3982 // If the corresponding selection was empty when this slice of the
3983 // clipboard text was written, then the entire line containing the
3984 // selection was copied. If this selection is also currently empty,
3985 // then paste the line before the current line of the buffer.
3986 let range = if selection.is_empty() && !line_mode && entire_line {
3987 let column = selection.start.to_point(&snapshot).column as usize;
3988 let line_start = selection.start - column;
3989 line_start..line_start
3990 } else {
3991 selection.range()
3992 };
3993
3994 edits.push((range, to_insert));
3995 original_indent_columns.extend(original_indent_column);
3996 }
3997 drop(snapshot);
3998
3999 buffer.edit(
4000 edits,
4001 Some(AutoindentMode::Block {
4002 original_indent_columns,
4003 }),
4004 cx,
4005 );
4006 });
4007
4008 let selections = this.selections.all::<usize>(cx);
4009 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4010 } else {
4011 this.insert(&clipboard_text, cx);
4012 }
4013 }
4014 });
4015 }
4016
4017 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
4018 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
4019 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
4020 self.change_selections(None, cx, |s| {
4021 s.select_anchors(selections.to_vec());
4022 });
4023 }
4024 self.request_autoscroll(Autoscroll::fit(), cx);
4025 self.unmark_text(cx);
4026 self.refresh_copilot_suggestions(cx);
4027 cx.emit(Event::Edited);
4028 }
4029 }
4030
4031 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
4032 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
4033 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
4034 {
4035 self.change_selections(None, cx, |s| {
4036 s.select_anchors(selections.to_vec());
4037 });
4038 }
4039 self.request_autoscroll(Autoscroll::fit(), cx);
4040 self.unmark_text(cx);
4041 self.refresh_copilot_suggestions(cx);
4042 cx.emit(Event::Edited);
4043 }
4044 }
4045
4046 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
4047 self.buffer
4048 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
4049 }
4050
4051 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
4052 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4053 let line_mode = s.line_mode;
4054 s.move_with(|map, selection| {
4055 let cursor = if selection.is_empty() && !line_mode {
4056 movement::left(map, selection.start)
4057 } else {
4058 selection.start
4059 };
4060 selection.collapse_to(cursor, SelectionGoal::None);
4061 });
4062 })
4063 }
4064
4065 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
4066 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4067 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
4068 })
4069 }
4070
4071 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
4072 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4073 let line_mode = s.line_mode;
4074 s.move_with(|map, selection| {
4075 let cursor = if selection.is_empty() && !line_mode {
4076 movement::right(map, selection.end)
4077 } else {
4078 selection.end
4079 };
4080 selection.collapse_to(cursor, SelectionGoal::None)
4081 });
4082 })
4083 }
4084
4085 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
4086 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4087 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
4088 })
4089 }
4090
4091 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
4092 if self.take_rename(true, cx).is_some() {
4093 return;
4094 }
4095
4096 if let Some(context_menu) = self.context_menu.as_mut() {
4097 if context_menu.select_prev(cx) {
4098 return;
4099 }
4100 }
4101
4102 if matches!(self.mode, EditorMode::SingleLine) {
4103 cx.propagate_action();
4104 return;
4105 }
4106
4107 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4108 let line_mode = s.line_mode;
4109 s.move_with(|map, selection| {
4110 if !selection.is_empty() && !line_mode {
4111 selection.goal = SelectionGoal::None;
4112 }
4113 let (cursor, goal) = movement::up(map, selection.start, selection.goal, false);
4114 selection.collapse_to(cursor, goal);
4115 });
4116 })
4117 }
4118
4119 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
4120 if self.take_rename(true, cx).is_some() {
4121 return;
4122 }
4123
4124 if self
4125 .context_menu
4126 .as_mut()
4127 .map(|menu| menu.select_first(cx))
4128 .unwrap_or(false)
4129 {
4130 return;
4131 }
4132
4133 if matches!(self.mode, EditorMode::SingleLine) {
4134 cx.propagate_action();
4135 return;
4136 }
4137
4138 let row_count = if let Some(row_count) = self.visible_line_count() {
4139 row_count as u32 - 1
4140 } else {
4141 return;
4142 };
4143
4144 let autoscroll = if action.center_cursor {
4145 Autoscroll::center()
4146 } else {
4147 Autoscroll::fit()
4148 };
4149
4150 self.change_selections(Some(autoscroll), cx, |s| {
4151 let line_mode = s.line_mode;
4152 s.move_with(|map, selection| {
4153 if !selection.is_empty() && !line_mode {
4154 selection.goal = SelectionGoal::None;
4155 }
4156 let (cursor, goal) =
4157 movement::up_by_rows(map, selection.end, row_count, selection.goal, false);
4158 selection.collapse_to(cursor, goal);
4159 });
4160 });
4161 }
4162
4163 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
4164 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4165 s.move_heads_with(|map, head, goal| movement::up(map, head, goal, false))
4166 })
4167 }
4168
4169 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
4170 self.take_rename(true, cx);
4171
4172 if let Some(context_menu) = self.context_menu.as_mut() {
4173 if context_menu.select_next(cx) {
4174 return;
4175 }
4176 }
4177
4178 if self.mode == EditorMode::SingleLine {
4179 cx.propagate_action();
4180 return;
4181 }
4182
4183 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4184 let line_mode = s.line_mode;
4185 s.move_with(|map, selection| {
4186 if !selection.is_empty() && !line_mode {
4187 selection.goal = SelectionGoal::None;
4188 }
4189 let (cursor, goal) = movement::down(map, selection.end, selection.goal, false);
4190 selection.collapse_to(cursor, goal);
4191 });
4192 });
4193 }
4194
4195 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
4196 if self.take_rename(true, cx).is_some() {
4197 return;
4198 }
4199
4200 if self
4201 .context_menu
4202 .as_mut()
4203 .map(|menu| menu.select_last(cx))
4204 .unwrap_or(false)
4205 {
4206 return;
4207 }
4208
4209 if matches!(self.mode, EditorMode::SingleLine) {
4210 cx.propagate_action();
4211 return;
4212 }
4213
4214 let row_count = if let Some(row_count) = self.visible_line_count() {
4215 row_count as u32 - 1
4216 } else {
4217 return;
4218 };
4219
4220 let autoscroll = if action.center_cursor {
4221 Autoscroll::center()
4222 } else {
4223 Autoscroll::fit()
4224 };
4225
4226 self.change_selections(Some(autoscroll), cx, |s| {
4227 let line_mode = s.line_mode;
4228 s.move_with(|map, selection| {
4229 if !selection.is_empty() && !line_mode {
4230 selection.goal = SelectionGoal::None;
4231 }
4232 let (cursor, goal) =
4233 movement::down_by_rows(map, selection.end, row_count, selection.goal, false);
4234 selection.collapse_to(cursor, goal);
4235 });
4236 });
4237 }
4238
4239 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
4240 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4241 s.move_heads_with(|map, head, goal| movement::down(map, head, goal, false))
4242 });
4243 }
4244
4245 pub fn move_to_previous_word_start(
4246 &mut self,
4247 _: &MoveToPreviousWordStart,
4248 cx: &mut ViewContext<Self>,
4249 ) {
4250 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4251 s.move_cursors_with(|map, head, _| {
4252 (
4253 movement::previous_word_start(map, head),
4254 SelectionGoal::None,
4255 )
4256 });
4257 })
4258 }
4259
4260 pub fn move_to_previous_subword_start(
4261 &mut self,
4262 _: &MoveToPreviousSubwordStart,
4263 cx: &mut ViewContext<Self>,
4264 ) {
4265 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4266 s.move_cursors_with(|map, head, _| {
4267 (
4268 movement::previous_subword_start(map, head),
4269 SelectionGoal::None,
4270 )
4271 });
4272 })
4273 }
4274
4275 pub fn select_to_previous_word_start(
4276 &mut self,
4277 _: &SelectToPreviousWordStart,
4278 cx: &mut ViewContext<Self>,
4279 ) {
4280 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4281 s.move_heads_with(|map, head, _| {
4282 (
4283 movement::previous_word_start(map, head),
4284 SelectionGoal::None,
4285 )
4286 });
4287 })
4288 }
4289
4290 pub fn select_to_previous_subword_start(
4291 &mut self,
4292 _: &SelectToPreviousSubwordStart,
4293 cx: &mut ViewContext<Self>,
4294 ) {
4295 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4296 s.move_heads_with(|map, head, _| {
4297 (
4298 movement::previous_subword_start(map, head),
4299 SelectionGoal::None,
4300 )
4301 });
4302 })
4303 }
4304
4305 pub fn delete_to_previous_word_start(
4306 &mut self,
4307 _: &DeleteToPreviousWordStart,
4308 cx: &mut ViewContext<Self>,
4309 ) {
4310 self.transact(cx, |this, cx| {
4311 this.select_autoclose_pair(cx);
4312 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4313 let line_mode = s.line_mode;
4314 s.move_with(|map, selection| {
4315 if selection.is_empty() && !line_mode {
4316 let cursor = movement::previous_word_start(map, selection.head());
4317 selection.set_head(cursor, SelectionGoal::None);
4318 }
4319 });
4320 });
4321 this.insert("", cx);
4322 });
4323 }
4324
4325 pub fn delete_to_previous_subword_start(
4326 &mut self,
4327 _: &DeleteToPreviousSubwordStart,
4328 cx: &mut ViewContext<Self>,
4329 ) {
4330 self.transact(cx, |this, cx| {
4331 this.select_autoclose_pair(cx);
4332 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4333 let line_mode = s.line_mode;
4334 s.move_with(|map, selection| {
4335 if selection.is_empty() && !line_mode {
4336 let cursor = movement::previous_subword_start(map, selection.head());
4337 selection.set_head(cursor, SelectionGoal::None);
4338 }
4339 });
4340 });
4341 this.insert("", cx);
4342 });
4343 }
4344
4345 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
4346 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4347 s.move_cursors_with(|map, head, _| {
4348 (movement::next_word_end(map, head), SelectionGoal::None)
4349 });
4350 })
4351 }
4352
4353 pub fn move_to_next_subword_end(
4354 &mut self,
4355 _: &MoveToNextSubwordEnd,
4356 cx: &mut ViewContext<Self>,
4357 ) {
4358 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4359 s.move_cursors_with(|map, head, _| {
4360 (movement::next_subword_end(map, head), SelectionGoal::None)
4361 });
4362 })
4363 }
4364
4365 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
4366 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4367 s.move_heads_with(|map, head, _| {
4368 (movement::next_word_end(map, head), SelectionGoal::None)
4369 });
4370 })
4371 }
4372
4373 pub fn select_to_next_subword_end(
4374 &mut self,
4375 _: &SelectToNextSubwordEnd,
4376 cx: &mut ViewContext<Self>,
4377 ) {
4378 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4379 s.move_heads_with(|map, head, _| {
4380 (movement::next_subword_end(map, head), SelectionGoal::None)
4381 });
4382 })
4383 }
4384
4385 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
4386 self.transact(cx, |this, cx| {
4387 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4388 let line_mode = s.line_mode;
4389 s.move_with(|map, selection| {
4390 if selection.is_empty() && !line_mode {
4391 let cursor = movement::next_word_end(map, selection.head());
4392 selection.set_head(cursor, SelectionGoal::None);
4393 }
4394 });
4395 });
4396 this.insert("", cx);
4397 });
4398 }
4399
4400 pub fn delete_to_next_subword_end(
4401 &mut self,
4402 _: &DeleteToNextSubwordEnd,
4403 cx: &mut ViewContext<Self>,
4404 ) {
4405 self.transact(cx, |this, cx| {
4406 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4407 s.move_with(|map, selection| {
4408 if selection.is_empty() {
4409 let cursor = movement::next_subword_end(map, selection.head());
4410 selection.set_head(cursor, SelectionGoal::None);
4411 }
4412 });
4413 });
4414 this.insert("", cx);
4415 });
4416 }
4417
4418 pub fn move_to_beginning_of_line(
4419 &mut self,
4420 _: &MoveToBeginningOfLine,
4421 cx: &mut ViewContext<Self>,
4422 ) {
4423 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4424 s.move_cursors_with(|map, head, _| {
4425 (
4426 movement::indented_line_beginning(map, head, true),
4427 SelectionGoal::None,
4428 )
4429 });
4430 })
4431 }
4432
4433 pub fn select_to_beginning_of_line(
4434 &mut self,
4435 action: &SelectToBeginningOfLine,
4436 cx: &mut ViewContext<Self>,
4437 ) {
4438 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4439 s.move_heads_with(|map, head, _| {
4440 (
4441 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
4442 SelectionGoal::None,
4443 )
4444 });
4445 });
4446 }
4447
4448 pub fn delete_to_beginning_of_line(
4449 &mut self,
4450 _: &DeleteToBeginningOfLine,
4451 cx: &mut ViewContext<Self>,
4452 ) {
4453 self.transact(cx, |this, cx| {
4454 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4455 s.move_with(|_, selection| {
4456 selection.reversed = true;
4457 });
4458 });
4459
4460 this.select_to_beginning_of_line(
4461 &SelectToBeginningOfLine {
4462 stop_at_soft_wraps: false,
4463 },
4464 cx,
4465 );
4466 this.backspace(&Backspace, cx);
4467 });
4468 }
4469
4470 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
4471 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4472 s.move_cursors_with(|map, head, _| {
4473 (movement::line_end(map, head, true), SelectionGoal::None)
4474 });
4475 })
4476 }
4477
4478 pub fn select_to_end_of_line(
4479 &mut self,
4480 action: &SelectToEndOfLine,
4481 cx: &mut ViewContext<Self>,
4482 ) {
4483 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4484 s.move_heads_with(|map, head, _| {
4485 (
4486 movement::line_end(map, head, action.stop_at_soft_wraps),
4487 SelectionGoal::None,
4488 )
4489 });
4490 })
4491 }
4492
4493 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
4494 self.transact(cx, |this, cx| {
4495 this.select_to_end_of_line(
4496 &SelectToEndOfLine {
4497 stop_at_soft_wraps: false,
4498 },
4499 cx,
4500 );
4501 this.delete(&Delete, cx);
4502 });
4503 }
4504
4505 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
4506 self.transact(cx, |this, cx| {
4507 this.select_to_end_of_line(
4508 &SelectToEndOfLine {
4509 stop_at_soft_wraps: false,
4510 },
4511 cx,
4512 );
4513 this.cut(&Cut, cx);
4514 });
4515 }
4516
4517 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
4518 if matches!(self.mode, EditorMode::SingleLine) {
4519 cx.propagate_action();
4520 return;
4521 }
4522
4523 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4524 s.select_ranges(vec![0..0]);
4525 });
4526 }
4527
4528 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
4529 let mut selection = self.selections.last::<Point>(cx);
4530 selection.set_head(Point::zero(), SelectionGoal::None);
4531
4532 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4533 s.select(vec![selection]);
4534 });
4535 }
4536
4537 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
4538 if matches!(self.mode, EditorMode::SingleLine) {
4539 cx.propagate_action();
4540 return;
4541 }
4542
4543 let cursor = self.buffer.read(cx).read(cx).len();
4544 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4545 s.select_ranges(vec![cursor..cursor])
4546 });
4547 }
4548
4549 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
4550 self.nav_history = nav_history;
4551 }
4552
4553 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
4554 self.nav_history.as_ref()
4555 }
4556
4557 fn push_to_nav_history(
4558 &self,
4559 cursor_anchor: Anchor,
4560 new_position: Option<Point>,
4561 cx: &mut ViewContext<Self>,
4562 ) {
4563 if let Some(nav_history) = &self.nav_history {
4564 let buffer = self.buffer.read(cx).read(cx);
4565 let cursor_position = cursor_anchor.to_point(&buffer);
4566 let scroll_state = self.scroll_manager.anchor();
4567 let scroll_top_row = scroll_state.top_row(&buffer);
4568 drop(buffer);
4569
4570 if let Some(new_position) = new_position {
4571 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
4572 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
4573 return;
4574 }
4575 }
4576
4577 nav_history.push(
4578 Some(NavigationData {
4579 cursor_anchor,
4580 cursor_position,
4581 scroll_anchor: scroll_state,
4582 scroll_top_row,
4583 }),
4584 cx,
4585 );
4586 }
4587 }
4588
4589 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
4590 let buffer = self.buffer.read(cx).snapshot(cx);
4591 let mut selection = self.selections.first::<usize>(cx);
4592 selection.set_head(buffer.len(), SelectionGoal::None);
4593 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4594 s.select(vec![selection]);
4595 });
4596 }
4597
4598 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
4599 let end = self.buffer.read(cx).read(cx).len();
4600 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4601 s.select_ranges(vec![0..end]);
4602 });
4603 }
4604
4605 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
4606 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4607 let mut selections = self.selections.all::<Point>(cx);
4608 let max_point = display_map.buffer_snapshot.max_point();
4609 for selection in &mut selections {
4610 let rows = selection.spanned_rows(true, &display_map);
4611 selection.start = Point::new(rows.start, 0);
4612 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
4613 selection.reversed = false;
4614 }
4615 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4616 s.select(selections);
4617 });
4618 }
4619
4620 pub fn split_selection_into_lines(
4621 &mut self,
4622 _: &SplitSelectionIntoLines,
4623 cx: &mut ViewContext<Self>,
4624 ) {
4625 let mut to_unfold = Vec::new();
4626 let mut new_selection_ranges = Vec::new();
4627 {
4628 let selections = self.selections.all::<Point>(cx);
4629 let buffer = self.buffer.read(cx).read(cx);
4630 for selection in selections {
4631 for row in selection.start.row..selection.end.row {
4632 let cursor = Point::new(row, buffer.line_len(row));
4633 new_selection_ranges.push(cursor..cursor);
4634 }
4635 new_selection_ranges.push(selection.end..selection.end);
4636 to_unfold.push(selection.start..selection.end);
4637 }
4638 }
4639 self.unfold_ranges(to_unfold, true, true, cx);
4640 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4641 s.select_ranges(new_selection_ranges);
4642 });
4643 }
4644
4645 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
4646 self.add_selection(true, cx);
4647 }
4648
4649 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
4650 self.add_selection(false, cx);
4651 }
4652
4653 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
4654 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4655 let mut selections = self.selections.all::<Point>(cx);
4656 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
4657 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
4658 let range = oldest_selection.display_range(&display_map).sorted();
4659 let columns = cmp::min(range.start.column(), range.end.column())
4660 ..cmp::max(range.start.column(), range.end.column());
4661
4662 selections.clear();
4663 let mut stack = Vec::new();
4664 for row in range.start.row()..=range.end.row() {
4665 if let Some(selection) = self.selections.build_columnar_selection(
4666 &display_map,
4667 row,
4668 &columns,
4669 oldest_selection.reversed,
4670 ) {
4671 stack.push(selection.id);
4672 selections.push(selection);
4673 }
4674 }
4675
4676 if above {
4677 stack.reverse();
4678 }
4679
4680 AddSelectionsState { above, stack }
4681 });
4682
4683 let last_added_selection = *state.stack.last().unwrap();
4684 let mut new_selections = Vec::new();
4685 if above == state.above {
4686 let end_row = if above {
4687 0
4688 } else {
4689 display_map.max_point().row()
4690 };
4691
4692 'outer: for selection in selections {
4693 if selection.id == last_added_selection {
4694 let range = selection.display_range(&display_map).sorted();
4695 debug_assert_eq!(range.start.row(), range.end.row());
4696 let mut row = range.start.row();
4697 let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
4698 {
4699 start..end
4700 } else {
4701 cmp::min(range.start.column(), range.end.column())
4702 ..cmp::max(range.start.column(), range.end.column())
4703 };
4704
4705 while row != end_row {
4706 if above {
4707 row -= 1;
4708 } else {
4709 row += 1;
4710 }
4711
4712 if let Some(new_selection) = self.selections.build_columnar_selection(
4713 &display_map,
4714 row,
4715 &columns,
4716 selection.reversed,
4717 ) {
4718 state.stack.push(new_selection.id);
4719 if above {
4720 new_selections.push(new_selection);
4721 new_selections.push(selection);
4722 } else {
4723 new_selections.push(selection);
4724 new_selections.push(new_selection);
4725 }
4726
4727 continue 'outer;
4728 }
4729 }
4730 }
4731
4732 new_selections.push(selection);
4733 }
4734 } else {
4735 new_selections = selections;
4736 new_selections.retain(|s| s.id != last_added_selection);
4737 state.stack.pop();
4738 }
4739
4740 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4741 s.select(new_selections);
4742 });
4743 if state.stack.len() > 1 {
4744 self.add_selections_state = Some(state);
4745 }
4746 }
4747
4748 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) {
4749 self.push_to_selection_history();
4750 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4751 let buffer = &display_map.buffer_snapshot;
4752 let mut selections = self.selections.all::<usize>(cx);
4753 if let Some(mut select_next_state) = self.select_next_state.take() {
4754 let query = &select_next_state.query;
4755 if !select_next_state.done {
4756 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
4757 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
4758 let mut next_selected_range = None;
4759
4760 let bytes_after_last_selection =
4761 buffer.bytes_in_range(last_selection.end..buffer.len());
4762 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
4763 let query_matches = query
4764 .stream_find_iter(bytes_after_last_selection)
4765 .map(|result| (last_selection.end, result))
4766 .chain(
4767 query
4768 .stream_find_iter(bytes_before_first_selection)
4769 .map(|result| (0, result)),
4770 );
4771 for (start_offset, query_match) in query_matches {
4772 let query_match = query_match.unwrap(); // can only fail due to I/O
4773 let offset_range =
4774 start_offset + query_match.start()..start_offset + query_match.end();
4775 let display_range = offset_range.start.to_display_point(&display_map)
4776 ..offset_range.end.to_display_point(&display_map);
4777
4778 if !select_next_state.wordwise
4779 || (!movement::is_inside_word(&display_map, display_range.start)
4780 && !movement::is_inside_word(&display_map, display_range.end))
4781 {
4782 next_selected_range = Some(offset_range);
4783 break;
4784 }
4785 }
4786
4787 if let Some(next_selected_range) = next_selected_range {
4788 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
4789 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
4790 if action.replace_newest {
4791 s.delete(s.newest_anchor().id);
4792 }
4793 s.insert_range(next_selected_range);
4794 });
4795 } else {
4796 select_next_state.done = true;
4797 }
4798 }
4799
4800 self.select_next_state = Some(select_next_state);
4801 } else if selections.len() == 1 {
4802 let selection = selections.last_mut().unwrap();
4803 if selection.start == selection.end {
4804 let word_range = movement::surrounding_word(
4805 &display_map,
4806 selection.start.to_display_point(&display_map),
4807 );
4808 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
4809 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
4810 selection.goal = SelectionGoal::None;
4811 selection.reversed = false;
4812
4813 let query = buffer
4814 .text_for_range(selection.start..selection.end)
4815 .collect::<String>();
4816 let select_state = SelectNextState {
4817 query: AhoCorasick::new_auto_configured(&[query]),
4818 wordwise: true,
4819 done: false,
4820 };
4821 self.unfold_ranges([selection.start..selection.end], false, true, cx);
4822 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
4823 s.select(selections);
4824 });
4825 self.select_next_state = Some(select_state);
4826 } else {
4827 let query = buffer
4828 .text_for_range(selection.start..selection.end)
4829 .collect::<String>();
4830 self.select_next_state = Some(SelectNextState {
4831 query: AhoCorasick::new_auto_configured(&[query]),
4832 wordwise: false,
4833 done: false,
4834 });
4835 self.select_next(action, cx);
4836 }
4837 }
4838 }
4839
4840 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
4841 self.transact(cx, |this, cx| {
4842 let mut selections = this.selections.all::<Point>(cx);
4843 let mut edits = Vec::new();
4844 let mut selection_edit_ranges = Vec::new();
4845 let mut last_toggled_row = None;
4846 let snapshot = this.buffer.read(cx).read(cx);
4847 let empty_str: Arc<str> = "".into();
4848 let mut suffixes_inserted = Vec::new();
4849
4850 fn comment_prefix_range(
4851 snapshot: &MultiBufferSnapshot,
4852 row: u32,
4853 comment_prefix: &str,
4854 comment_prefix_whitespace: &str,
4855 ) -> Range<Point> {
4856 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
4857
4858 let mut line_bytes = snapshot
4859 .bytes_in_range(start..snapshot.max_point())
4860 .flatten()
4861 .copied();
4862
4863 // If this line currently begins with the line comment prefix, then record
4864 // the range containing the prefix.
4865 if line_bytes
4866 .by_ref()
4867 .take(comment_prefix.len())
4868 .eq(comment_prefix.bytes())
4869 {
4870 // Include any whitespace that matches the comment prefix.
4871 let matching_whitespace_len = line_bytes
4872 .zip(comment_prefix_whitespace.bytes())
4873 .take_while(|(a, b)| a == b)
4874 .count() as u32;
4875 let end = Point::new(
4876 start.row,
4877 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
4878 );
4879 start..end
4880 } else {
4881 start..start
4882 }
4883 }
4884
4885 fn comment_suffix_range(
4886 snapshot: &MultiBufferSnapshot,
4887 row: u32,
4888 comment_suffix: &str,
4889 comment_suffix_has_leading_space: bool,
4890 ) -> Range<Point> {
4891 let end = Point::new(row, snapshot.line_len(row));
4892 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
4893
4894 let mut line_end_bytes = snapshot
4895 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
4896 .flatten()
4897 .copied();
4898
4899 let leading_space_len = if suffix_start_column > 0
4900 && line_end_bytes.next() == Some(b' ')
4901 && comment_suffix_has_leading_space
4902 {
4903 1
4904 } else {
4905 0
4906 };
4907
4908 // If this line currently begins with the line comment prefix, then record
4909 // the range containing the prefix.
4910 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
4911 let start = Point::new(end.row, suffix_start_column - leading_space_len);
4912 start..end
4913 } else {
4914 end..end
4915 }
4916 }
4917
4918 // TODO: Handle selections that cross excerpts
4919 for selection in &mut selections {
4920 let start_column = snapshot.indent_size_for_line(selection.start.row).len;
4921 let language = if let Some(language) =
4922 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
4923 {
4924 language
4925 } else {
4926 continue;
4927 };
4928
4929 selection_edit_ranges.clear();
4930
4931 // If multiple selections contain a given row, avoid processing that
4932 // row more than once.
4933 let mut start_row = selection.start.row;
4934 if last_toggled_row == Some(start_row) {
4935 start_row += 1;
4936 }
4937 let end_row =
4938 if selection.end.row > selection.start.row && selection.end.column == 0 {
4939 selection.end.row - 1
4940 } else {
4941 selection.end.row
4942 };
4943 last_toggled_row = Some(end_row);
4944
4945 if start_row > end_row {
4946 continue;
4947 }
4948
4949 // If the language has line comments, toggle those.
4950 if let Some(full_comment_prefix) = language.line_comment_prefix() {
4951 // Split the comment prefix's trailing whitespace into a separate string,
4952 // as that portion won't be used for detecting if a line is a comment.
4953 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
4954 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
4955 let mut all_selection_lines_are_comments = true;
4956
4957 for row in start_row..=end_row {
4958 if snapshot.is_line_blank(row) {
4959 continue;
4960 }
4961
4962 let prefix_range = comment_prefix_range(
4963 snapshot.deref(),
4964 row,
4965 comment_prefix,
4966 comment_prefix_whitespace,
4967 );
4968 if prefix_range.is_empty() {
4969 all_selection_lines_are_comments = false;
4970 }
4971 selection_edit_ranges.push(prefix_range);
4972 }
4973
4974 if all_selection_lines_are_comments {
4975 edits.extend(
4976 selection_edit_ranges
4977 .iter()
4978 .cloned()
4979 .map(|range| (range, empty_str.clone())),
4980 );
4981 } else {
4982 let min_column = selection_edit_ranges
4983 .iter()
4984 .map(|r| r.start.column)
4985 .min()
4986 .unwrap_or(0);
4987 edits.extend(selection_edit_ranges.iter().map(|range| {
4988 let position = Point::new(range.start.row, min_column);
4989 (position..position, full_comment_prefix.clone())
4990 }));
4991 }
4992 } else if let Some((full_comment_prefix, comment_suffix)) =
4993 language.block_comment_delimiters()
4994 {
4995 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
4996 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
4997 let prefix_range = comment_prefix_range(
4998 snapshot.deref(),
4999 start_row,
5000 comment_prefix,
5001 comment_prefix_whitespace,
5002 );
5003 let suffix_range = comment_suffix_range(
5004 snapshot.deref(),
5005 end_row,
5006 comment_suffix.trim_start_matches(' '),
5007 comment_suffix.starts_with(' '),
5008 );
5009
5010 if prefix_range.is_empty() || suffix_range.is_empty() {
5011 edits.push((
5012 prefix_range.start..prefix_range.start,
5013 full_comment_prefix.clone(),
5014 ));
5015 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
5016 suffixes_inserted.push((end_row, comment_suffix.len()));
5017 } else {
5018 edits.push((prefix_range, empty_str.clone()));
5019 edits.push((suffix_range, empty_str.clone()));
5020 }
5021 } else {
5022 continue;
5023 }
5024 }
5025
5026 drop(snapshot);
5027 this.buffer.update(cx, |buffer, cx| {
5028 buffer.edit(edits, None, cx);
5029 });
5030
5031 // Adjust selections so that they end before any comment suffixes that
5032 // were inserted.
5033 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
5034 let mut selections = this.selections.all::<Point>(cx);
5035 let snapshot = this.buffer.read(cx).read(cx);
5036 for selection in &mut selections {
5037 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
5038 match row.cmp(&selection.end.row) {
5039 Ordering::Less => {
5040 suffixes_inserted.next();
5041 continue;
5042 }
5043 Ordering::Greater => break,
5044 Ordering::Equal => {
5045 if selection.end.column == snapshot.line_len(row) {
5046 if selection.is_empty() {
5047 selection.start.column -= suffix_len as u32;
5048 }
5049 selection.end.column -= suffix_len as u32;
5050 }
5051 break;
5052 }
5053 }
5054 }
5055 }
5056
5057 drop(snapshot);
5058 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5059
5060 let selections = this.selections.all::<Point>(cx);
5061 let selections_on_single_row = selections.windows(2).all(|selections| {
5062 selections[0].start.row == selections[1].start.row
5063 && selections[0].end.row == selections[1].end.row
5064 && selections[0].start.row == selections[0].end.row
5065 });
5066 let selections_selecting = selections
5067 .iter()
5068 .any(|selection| selection.start != selection.end);
5069 let advance_downwards = action.advance_downwards
5070 && selections_on_single_row
5071 && !selections_selecting
5072 && this.mode != EditorMode::SingleLine;
5073
5074 if advance_downwards {
5075 let snapshot = this.buffer.read(cx).snapshot(cx);
5076
5077 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5078 s.move_cursors_with(|display_snapshot, display_point, _| {
5079 let mut point = display_point.to_point(display_snapshot);
5080 point.row += 1;
5081 point = snapshot.clip_point(point, Bias::Left);
5082 let display_point = point.to_display_point(display_snapshot);
5083 (display_point, SelectionGoal::Column(display_point.column()))
5084 })
5085 });
5086 }
5087 });
5088 }
5089
5090 pub fn select_larger_syntax_node(
5091 &mut self,
5092 _: &SelectLargerSyntaxNode,
5093 cx: &mut ViewContext<Self>,
5094 ) {
5095 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5096 let buffer = self.buffer.read(cx).snapshot(cx);
5097 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
5098
5099 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
5100 let mut selected_larger_node = false;
5101 let new_selections = old_selections
5102 .iter()
5103 .map(|selection| {
5104 let old_range = selection.start..selection.end;
5105 let mut new_range = old_range.clone();
5106 while let Some(containing_range) =
5107 buffer.range_for_syntax_ancestor(new_range.clone())
5108 {
5109 new_range = containing_range;
5110 if !display_map.intersects_fold(new_range.start)
5111 && !display_map.intersects_fold(new_range.end)
5112 {
5113 break;
5114 }
5115 }
5116
5117 selected_larger_node |= new_range != old_range;
5118 Selection {
5119 id: selection.id,
5120 start: new_range.start,
5121 end: new_range.end,
5122 goal: SelectionGoal::None,
5123 reversed: selection.reversed,
5124 }
5125 })
5126 .collect::<Vec<_>>();
5127
5128 if selected_larger_node {
5129 stack.push(old_selections);
5130 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5131 s.select(new_selections);
5132 });
5133 }
5134 self.select_larger_syntax_node_stack = stack;
5135 }
5136
5137 pub fn select_smaller_syntax_node(
5138 &mut self,
5139 _: &SelectSmallerSyntaxNode,
5140 cx: &mut ViewContext<Self>,
5141 ) {
5142 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
5143 if let Some(selections) = stack.pop() {
5144 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5145 s.select(selections.to_vec());
5146 });
5147 }
5148 self.select_larger_syntax_node_stack = stack;
5149 }
5150
5151 pub fn move_to_enclosing_bracket(
5152 &mut self,
5153 _: &MoveToEnclosingBracket,
5154 cx: &mut ViewContext<Self>,
5155 ) {
5156 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5157 s.move_offsets_with(|snapshot, selection| {
5158 let Some(enclosing_bracket_ranges) = snapshot.enclosing_bracket_ranges(selection.start..selection.end) else {
5159 return;
5160 };
5161
5162 let mut best_length = usize::MAX;
5163 let mut best_inside = false;
5164 let mut best_in_bracket_range = false;
5165 let mut best_destination = None;
5166 for (open, close) in enclosing_bracket_ranges {
5167 let close = close.to_inclusive();
5168 let length = close.end() - open.start;
5169 let inside = selection.start >= open.end && selection.end <= *close.start();
5170 let in_bracket_range = open.to_inclusive().contains(&selection.head()) || close.contains(&selection.head());
5171
5172 // If best is next to a bracket and current isn't, skip
5173 if !in_bracket_range && best_in_bracket_range {
5174 continue;
5175 }
5176
5177 // Prefer smaller lengths unless best is inside and current isn't
5178 if length > best_length && (best_inside || !inside) {
5179 continue;
5180 }
5181
5182 best_length = length;
5183 best_inside = inside;
5184 best_in_bracket_range = in_bracket_range;
5185 best_destination = Some(if close.contains(&selection.start) && close.contains(&selection.end) {
5186 if inside {
5187 open.end
5188 } else {
5189 open.start
5190 }
5191 } else {
5192 if inside {
5193 *close.start()
5194 } else {
5195 *close.end()
5196 }
5197 });
5198 }
5199
5200 if let Some(destination) = best_destination {
5201 selection.collapse_to(destination, SelectionGoal::None);
5202 }
5203 })
5204 });
5205 }
5206
5207 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
5208 self.end_selection(cx);
5209 self.selection_history.mode = SelectionHistoryMode::Undoing;
5210 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
5211 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
5212 self.select_next_state = entry.select_next_state;
5213 self.add_selections_state = entry.add_selections_state;
5214 self.request_autoscroll(Autoscroll::newest(), cx);
5215 }
5216 self.selection_history.mode = SelectionHistoryMode::Normal;
5217 }
5218
5219 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
5220 self.end_selection(cx);
5221 self.selection_history.mode = SelectionHistoryMode::Redoing;
5222 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
5223 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
5224 self.select_next_state = entry.select_next_state;
5225 self.add_selections_state = entry.add_selections_state;
5226 self.request_autoscroll(Autoscroll::newest(), cx);
5227 }
5228 self.selection_history.mode = SelectionHistoryMode::Normal;
5229 }
5230
5231 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
5232 self.go_to_diagnostic_impl(Direction::Next, cx)
5233 }
5234
5235 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
5236 self.go_to_diagnostic_impl(Direction::Prev, cx)
5237 }
5238
5239 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
5240 let buffer = self.buffer.read(cx).snapshot(cx);
5241 let selection = self.selections.newest::<usize>(cx);
5242
5243 // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
5244 if direction == Direction::Next {
5245 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
5246 let (group_id, jump_to) = popover.activation_info();
5247 if self.activate_diagnostics(group_id, cx) {
5248 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5249 let mut new_selection = s.newest_anchor().clone();
5250 new_selection.collapse_to(jump_to, SelectionGoal::None);
5251 s.select_anchors(vec![new_selection.clone()]);
5252 });
5253 }
5254 return;
5255 }
5256 }
5257
5258 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
5259 active_diagnostics
5260 .primary_range
5261 .to_offset(&buffer)
5262 .to_inclusive()
5263 });
5264 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
5265 if active_primary_range.contains(&selection.head()) {
5266 *active_primary_range.end()
5267 } else {
5268 selection.head()
5269 }
5270 } else {
5271 selection.head()
5272 };
5273
5274 loop {
5275 let mut diagnostics = if direction == Direction::Prev {
5276 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
5277 } else {
5278 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
5279 };
5280 let group = diagnostics.find_map(|entry| {
5281 if entry.diagnostic.is_primary
5282 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
5283 && !entry.range.is_empty()
5284 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
5285 {
5286 Some((entry.range, entry.diagnostic.group_id))
5287 } else {
5288 None
5289 }
5290 });
5291
5292 if let Some((primary_range, group_id)) = group {
5293 if self.activate_diagnostics(group_id, cx) {
5294 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5295 s.select(vec![Selection {
5296 id: selection.id,
5297 start: primary_range.start,
5298 end: primary_range.start,
5299 reversed: false,
5300 goal: SelectionGoal::None,
5301 }]);
5302 });
5303 }
5304 break;
5305 } else {
5306 // Cycle around to the start of the buffer, potentially moving back to the start of
5307 // the currently active diagnostic.
5308 active_primary_range.take();
5309 if direction == Direction::Prev {
5310 if search_start == buffer.len() {
5311 break;
5312 } else {
5313 search_start = buffer.len();
5314 }
5315 } else if search_start == 0 {
5316 break;
5317 } else {
5318 search_start = 0;
5319 }
5320 }
5321 }
5322 }
5323
5324 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
5325 self.go_to_hunk_impl(Direction::Next, cx)
5326 }
5327
5328 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
5329 self.go_to_hunk_impl(Direction::Prev, cx)
5330 }
5331
5332 pub fn go_to_hunk_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
5333 let snapshot = self
5334 .display_map
5335 .update(cx, |display_map, cx| display_map.snapshot(cx));
5336 let selection = self.selections.newest::<Point>(cx);
5337
5338 fn seek_in_direction(
5339 this: &mut Editor,
5340 snapshot: &DisplaySnapshot,
5341 initial_point: Point,
5342 is_wrapped: bool,
5343 direction: Direction,
5344 cx: &mut ViewContext<Editor>,
5345 ) -> bool {
5346 let hunks = if direction == Direction::Next {
5347 snapshot
5348 .buffer_snapshot
5349 .git_diff_hunks_in_range(initial_point.row..u32::MAX, false)
5350 } else {
5351 snapshot
5352 .buffer_snapshot
5353 .git_diff_hunks_in_range(0..initial_point.row, true)
5354 };
5355
5356 let display_point = initial_point.to_display_point(snapshot);
5357 let mut hunks = hunks
5358 .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
5359 .skip_while(|hunk| {
5360 if is_wrapped {
5361 false
5362 } else {
5363 hunk.contains_display_row(display_point.row())
5364 }
5365 })
5366 .dedup();
5367
5368 if let Some(hunk) = hunks.next() {
5369 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5370 let row = hunk.start_display_row();
5371 let point = DisplayPoint::new(row, 0);
5372 s.select_display_ranges([point..point]);
5373 });
5374
5375 true
5376 } else {
5377 false
5378 }
5379 }
5380
5381 if !seek_in_direction(self, &snapshot, selection.head(), false, direction, cx) {
5382 let wrapped_point = match direction {
5383 Direction::Next => Point::zero(),
5384 Direction::Prev => snapshot.buffer_snapshot.max_point(),
5385 };
5386 seek_in_direction(self, &snapshot, wrapped_point, true, direction, cx);
5387 }
5388 }
5389
5390 pub fn go_to_definition(
5391 workspace: &mut Workspace,
5392 _: &GoToDefinition,
5393 cx: &mut ViewContext<Workspace>,
5394 ) {
5395 Self::go_to_definition_of_kind(GotoDefinitionKind::Symbol, workspace, cx);
5396 }
5397
5398 pub fn go_to_type_definition(
5399 workspace: &mut Workspace,
5400 _: &GoToTypeDefinition,
5401 cx: &mut ViewContext<Workspace>,
5402 ) {
5403 Self::go_to_definition_of_kind(GotoDefinitionKind::Type, workspace, cx);
5404 }
5405
5406 fn go_to_definition_of_kind(
5407 kind: GotoDefinitionKind,
5408 workspace: &mut Workspace,
5409 cx: &mut ViewContext<Workspace>,
5410 ) {
5411 let active_item = workspace.active_item(cx);
5412 let editor_handle = if let Some(editor) = active_item
5413 .as_ref()
5414 .and_then(|item| item.act_as::<Self>(cx))
5415 {
5416 editor
5417 } else {
5418 return;
5419 };
5420
5421 let editor = editor_handle.read(cx);
5422 let buffer = editor.buffer.read(cx);
5423 let head = editor.selections.newest::<usize>(cx).head();
5424 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
5425 text_anchor
5426 } else {
5427 return;
5428 };
5429
5430 let project = workspace.project().clone();
5431 let definitions = project.update(cx, |project, cx| match kind {
5432 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
5433 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
5434 });
5435
5436 cx.spawn_labeled("Fetching Definition...", |workspace, mut cx| async move {
5437 let definitions = definitions.await?;
5438 workspace.update(&mut cx, |workspace, cx| {
5439 Editor::navigate_to_definitions(workspace, editor_handle, definitions, cx);
5440 });
5441
5442 Ok::<(), anyhow::Error>(())
5443 })
5444 .detach_and_log_err(cx);
5445 }
5446
5447 pub fn navigate_to_definitions(
5448 workspace: &mut Workspace,
5449 editor_handle: ViewHandle<Editor>,
5450 definitions: Vec<LocationLink>,
5451 cx: &mut ViewContext<Workspace>,
5452 ) {
5453 let pane = workspace.active_pane().clone();
5454 // If there is one definition, just open it directly
5455 if let [definition] = definitions.as_slice() {
5456 let range = definition
5457 .target
5458 .range
5459 .to_offset(definition.target.buffer.read(cx));
5460
5461 let target_editor_handle =
5462 workspace.open_project_item(definition.target.buffer.clone(), cx);
5463 target_editor_handle.update(cx, |target_editor, cx| {
5464 // When selecting a definition in a different buffer, disable the nav history
5465 // to avoid creating a history entry at the previous cursor location.
5466 if editor_handle != target_editor_handle {
5467 pane.update(cx, |pane, _| pane.disable_history());
5468 }
5469 target_editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
5470 s.select_ranges([range]);
5471 });
5472
5473 pane.update(cx, |pane, _| pane.enable_history());
5474 });
5475 } else if !definitions.is_empty() {
5476 let replica_id = editor_handle.read(cx).replica_id(cx);
5477 let title = definitions
5478 .iter()
5479 .find(|definition| definition.origin.is_some())
5480 .and_then(|definition| {
5481 definition.origin.as_ref().map(|origin| {
5482 let buffer = origin.buffer.read(cx);
5483 format!(
5484 "Definitions for {}",
5485 buffer
5486 .text_for_range(origin.range.clone())
5487 .collect::<String>()
5488 )
5489 })
5490 })
5491 .unwrap_or("Definitions".to_owned());
5492 let locations = definitions
5493 .into_iter()
5494 .map(|definition| definition.target)
5495 .collect();
5496 Self::open_locations_in_multibuffer(workspace, locations, replica_id, title, cx)
5497 }
5498 }
5499
5500 pub fn find_all_references(
5501 workspace: &mut Workspace,
5502 _: &FindAllReferences,
5503 cx: &mut ViewContext<Workspace>,
5504 ) -> Option<Task<Result<()>>> {
5505 let active_item = workspace.active_item(cx)?;
5506 let editor_handle = active_item.act_as::<Self>(cx)?;
5507
5508 let editor = editor_handle.read(cx);
5509 let buffer = editor.buffer.read(cx);
5510 let head = editor.selections.newest::<usize>(cx).head();
5511 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
5512 let replica_id = editor.replica_id(cx);
5513
5514 let project = workspace.project().clone();
5515 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
5516 Some(cx.spawn_labeled(
5517 "Finding All References...",
5518 |workspace, mut cx| async move {
5519 let locations = references.await?;
5520 if locations.is_empty() {
5521 return Ok(());
5522 }
5523
5524 workspace.update(&mut cx, |workspace, cx| {
5525 let title = locations
5526 .first()
5527 .as_ref()
5528 .map(|location| {
5529 let buffer = location.buffer.read(cx);
5530 format!(
5531 "References to `{}`",
5532 buffer
5533 .text_for_range(location.range.clone())
5534 .collect::<String>()
5535 )
5536 })
5537 .unwrap();
5538 Self::open_locations_in_multibuffer(
5539 workspace, locations, replica_id, title, cx,
5540 );
5541 });
5542
5543 Ok(())
5544 },
5545 ))
5546 }
5547
5548 /// Opens a multibuffer with the given project locations in it
5549 pub fn open_locations_in_multibuffer(
5550 workspace: &mut Workspace,
5551 mut locations: Vec<Location>,
5552 replica_id: ReplicaId,
5553 title: String,
5554 cx: &mut ViewContext<Workspace>,
5555 ) {
5556 // If there are multiple definitions, open them in a multibuffer
5557 locations.sort_by_key(|location| location.buffer.id());
5558 let mut locations = locations.into_iter().peekable();
5559 let mut ranges_to_highlight = Vec::new();
5560
5561 let excerpt_buffer = cx.add_model(|cx| {
5562 let mut multibuffer = MultiBuffer::new(replica_id);
5563 while let Some(location) = locations.next() {
5564 let buffer = location.buffer.read(cx);
5565 let mut ranges_for_buffer = Vec::new();
5566 let range = location.range.to_offset(buffer);
5567 ranges_for_buffer.push(range.clone());
5568
5569 while let Some(next_location) = locations.peek() {
5570 if next_location.buffer == location.buffer {
5571 ranges_for_buffer.push(next_location.range.to_offset(buffer));
5572 locations.next();
5573 } else {
5574 break;
5575 }
5576 }
5577
5578 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
5579 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
5580 location.buffer.clone(),
5581 ranges_for_buffer,
5582 1,
5583 cx,
5584 ))
5585 }
5586
5587 multibuffer.with_title(title)
5588 });
5589
5590 let editor = cx.add_view(|cx| {
5591 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
5592 });
5593 editor.update(cx, |editor, cx| {
5594 editor.highlight_background::<Self>(
5595 ranges_to_highlight,
5596 |theme| theme.editor.highlighted_line_background,
5597 cx,
5598 );
5599 });
5600 workspace.add_item(Box::new(editor), cx);
5601 }
5602
5603 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
5604 use language::ToOffset as _;
5605
5606 let project = self.project.clone()?;
5607 let selection = self.selections.newest_anchor().clone();
5608 let (cursor_buffer, cursor_buffer_position) = self
5609 .buffer
5610 .read(cx)
5611 .text_anchor_for_position(selection.head(), cx)?;
5612 let (tail_buffer, _) = self
5613 .buffer
5614 .read(cx)
5615 .text_anchor_for_position(selection.tail(), cx)?;
5616 if tail_buffer != cursor_buffer {
5617 return None;
5618 }
5619
5620 let snapshot = cursor_buffer.read(cx).snapshot();
5621 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
5622 let prepare_rename = project.update(cx, |project, cx| {
5623 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
5624 });
5625
5626 Some(cx.spawn(|this, mut cx| async move {
5627 let rename_range = if let Some(range) = prepare_rename.await? {
5628 Some(range)
5629 } else {
5630 this.read_with(&cx, |this, cx| {
5631 let buffer = this.buffer.read(cx).snapshot(cx);
5632 let mut buffer_highlights = this
5633 .document_highlights_for_position(selection.head(), &buffer)
5634 .filter(|highlight| {
5635 highlight.start.excerpt_id() == selection.head().excerpt_id()
5636 && highlight.end.excerpt_id() == selection.head().excerpt_id()
5637 });
5638 buffer_highlights
5639 .next()
5640 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
5641 })
5642 };
5643 if let Some(rename_range) = rename_range {
5644 let rename_buffer_range = rename_range.to_offset(&snapshot);
5645 let cursor_offset_in_rename_range =
5646 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
5647
5648 this.update(&mut cx, |this, cx| {
5649 this.take_rename(false, cx);
5650 let style = this.style(cx);
5651 let buffer = this.buffer.read(cx).read(cx);
5652 let cursor_offset = selection.head().to_offset(&buffer);
5653 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
5654 let rename_end = rename_start + rename_buffer_range.len();
5655 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
5656 let mut old_highlight_id = None;
5657 let old_name: Arc<str> = buffer
5658 .chunks(rename_start..rename_end, true)
5659 .map(|chunk| {
5660 if old_highlight_id.is_none() {
5661 old_highlight_id = chunk.syntax_highlight_id;
5662 }
5663 chunk.text
5664 })
5665 .collect::<String>()
5666 .into();
5667
5668 drop(buffer);
5669
5670 // Position the selection in the rename editor so that it matches the current selection.
5671 this.show_local_selections = false;
5672 let rename_editor = cx.add_view(|cx| {
5673 let mut editor = Editor::single_line(None, cx);
5674 if let Some(old_highlight_id) = old_highlight_id {
5675 editor.override_text_style =
5676 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
5677 }
5678 editor.buffer.update(cx, |buffer, cx| {
5679 buffer.edit([(0..0, old_name.clone())], None, cx)
5680 });
5681 editor.select_all(&SelectAll, cx);
5682 editor
5683 });
5684
5685 let ranges = this
5686 .clear_background_highlights::<DocumentHighlightWrite>(cx)
5687 .into_iter()
5688 .flat_map(|(_, ranges)| ranges)
5689 .chain(
5690 this.clear_background_highlights::<DocumentHighlightRead>(cx)
5691 .into_iter()
5692 .flat_map(|(_, ranges)| ranges),
5693 )
5694 .collect();
5695
5696 this.highlight_text::<Rename>(
5697 ranges,
5698 HighlightStyle {
5699 fade_out: Some(style.rename_fade),
5700 ..Default::default()
5701 },
5702 cx,
5703 );
5704 cx.focus(&rename_editor);
5705 let block_id = this.insert_blocks(
5706 [BlockProperties {
5707 style: BlockStyle::Flex,
5708 position: range.start.clone(),
5709 height: 1,
5710 render: Arc::new({
5711 let editor = rename_editor.clone();
5712 move |cx: &mut BlockContext| {
5713 ChildView::new(&editor, cx)
5714 .contained()
5715 .with_padding_left(cx.anchor_x)
5716 .boxed()
5717 }
5718 }),
5719 disposition: BlockDisposition::Below,
5720 }],
5721 cx,
5722 )[0];
5723 this.pending_rename = Some(RenameState {
5724 range,
5725 old_name,
5726 editor: rename_editor,
5727 block_id,
5728 });
5729 });
5730 }
5731
5732 Ok(())
5733 }))
5734 }
5735
5736 pub fn confirm_rename(
5737 workspace: &mut Workspace,
5738 _: &ConfirmRename,
5739 cx: &mut ViewContext<Workspace>,
5740 ) -> Option<Task<Result<()>>> {
5741 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
5742
5743 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
5744 let rename = editor.take_rename(false, cx)?;
5745 let buffer = editor.buffer.read(cx);
5746 let (start_buffer, start) =
5747 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
5748 let (end_buffer, end) =
5749 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
5750 if start_buffer == end_buffer {
5751 let new_name = rename.editor.read(cx).text(cx);
5752 Some((start_buffer, start..end, rename.old_name, new_name))
5753 } else {
5754 None
5755 }
5756 })?;
5757
5758 let rename = workspace.project().clone().update(cx, |project, cx| {
5759 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
5760 });
5761
5762 Some(cx.spawn(|workspace, mut cx| async move {
5763 let project_transaction = rename.await?;
5764 Self::open_project_transaction(
5765 editor.clone(),
5766 workspace,
5767 project_transaction,
5768 format!("Rename: {} → {}", old_name, new_name),
5769 cx.clone(),
5770 )
5771 .await?;
5772
5773 editor.update(&mut cx, |editor, cx| {
5774 editor.refresh_document_highlights(cx);
5775 });
5776 Ok(())
5777 }))
5778 }
5779
5780 fn take_rename(
5781 &mut self,
5782 moving_cursor: bool,
5783 cx: &mut ViewContext<Self>,
5784 ) -> Option<RenameState> {
5785 let rename = self.pending_rename.take()?;
5786 self.remove_blocks([rename.block_id].into_iter().collect(), cx);
5787 self.clear_text_highlights::<Rename>(cx);
5788 self.show_local_selections = true;
5789
5790 if moving_cursor {
5791 let rename_editor = rename.editor.read(cx);
5792 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
5793
5794 // Update the selection to match the position of the selection inside
5795 // the rename editor.
5796 let snapshot = self.buffer.read(cx).read(cx);
5797 let rename_range = rename.range.to_offset(&snapshot);
5798 let cursor_in_editor = snapshot
5799 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
5800 .min(rename_range.end);
5801 drop(snapshot);
5802
5803 self.change_selections(None, cx, |s| {
5804 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
5805 });
5806 } else {
5807 self.refresh_document_highlights(cx);
5808 }
5809
5810 Some(rename)
5811 }
5812
5813 #[cfg(any(test, feature = "test-support"))]
5814 pub fn pending_rename(&self) -> Option<&RenameState> {
5815 self.pending_rename.as_ref()
5816 }
5817
5818 fn format(&mut self, _: &Format, cx: &mut ViewContext<'_, Self>) -> Option<Task<Result<()>>> {
5819 let project = match &self.project {
5820 Some(project) => project.clone(),
5821 None => return None,
5822 };
5823
5824 Some(self.perform_format(project, FormatTrigger::Manual, cx))
5825 }
5826
5827 fn perform_format(
5828 &mut self,
5829 project: ModelHandle<Project>,
5830 trigger: FormatTrigger,
5831 cx: &mut ViewContext<'_, Self>,
5832 ) -> Task<Result<()>> {
5833 let buffer = self.buffer().clone();
5834 let buffers = buffer.read(cx).all_buffers();
5835
5836 let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
5837 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
5838
5839 cx.spawn(|_, mut cx| async move {
5840 let transaction = futures::select_biased! {
5841 _ = timeout => {
5842 log::warn!("timed out waiting for formatting");
5843 None
5844 }
5845 transaction = format.log_err().fuse() => transaction,
5846 };
5847
5848 buffer.update(&mut cx, |buffer, cx| {
5849 if let Some(transaction) = transaction {
5850 if !buffer.is_singleton() {
5851 buffer.push_transaction(&transaction.0);
5852 }
5853 }
5854
5855 cx.notify();
5856 });
5857
5858 Ok(())
5859 })
5860 }
5861
5862 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
5863 if let Some(project) = self.project.clone() {
5864 self.buffer.update(cx, |multi_buffer, cx| {
5865 project.update(cx, |project, cx| {
5866 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
5867 });
5868 })
5869 }
5870 }
5871
5872 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
5873 cx.show_character_palette();
5874 }
5875
5876 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
5877 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
5878 let buffer = self.buffer.read(cx).snapshot(cx);
5879 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
5880 let is_valid = buffer
5881 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
5882 .any(|entry| {
5883 entry.diagnostic.is_primary
5884 && !entry.range.is_empty()
5885 && entry.range.start == primary_range_start
5886 && entry.diagnostic.message == active_diagnostics.primary_message
5887 });
5888
5889 if is_valid != active_diagnostics.is_valid {
5890 active_diagnostics.is_valid = is_valid;
5891 let mut new_styles = HashMap::default();
5892 for (block_id, diagnostic) in &active_diagnostics.blocks {
5893 new_styles.insert(
5894 *block_id,
5895 diagnostic_block_renderer(diagnostic.clone(), is_valid),
5896 );
5897 }
5898 self.display_map
5899 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
5900 }
5901 }
5902 }
5903
5904 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
5905 self.dismiss_diagnostics(cx);
5906 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
5907 let buffer = self.buffer.read(cx).snapshot(cx);
5908
5909 let mut primary_range = None;
5910 let mut primary_message = None;
5911 let mut group_end = Point::zero();
5912 let diagnostic_group = buffer
5913 .diagnostic_group::<Point>(group_id)
5914 .map(|entry| {
5915 if entry.range.end > group_end {
5916 group_end = entry.range.end;
5917 }
5918 if entry.diagnostic.is_primary {
5919 primary_range = Some(entry.range.clone());
5920 primary_message = Some(entry.diagnostic.message.clone());
5921 }
5922 entry
5923 })
5924 .collect::<Vec<_>>();
5925 let primary_range = primary_range?;
5926 let primary_message = primary_message?;
5927 let primary_range =
5928 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
5929
5930 let blocks = display_map
5931 .insert_blocks(
5932 diagnostic_group.iter().map(|entry| {
5933 let diagnostic = entry.diagnostic.clone();
5934 let message_height = diagnostic.message.lines().count() as u8;
5935 BlockProperties {
5936 style: BlockStyle::Fixed,
5937 position: buffer.anchor_after(entry.range.start),
5938 height: message_height,
5939 render: diagnostic_block_renderer(diagnostic, true),
5940 disposition: BlockDisposition::Below,
5941 }
5942 }),
5943 cx,
5944 )
5945 .into_iter()
5946 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
5947 .collect();
5948
5949 Some(ActiveDiagnosticGroup {
5950 primary_range,
5951 primary_message,
5952 blocks,
5953 is_valid: true,
5954 })
5955 });
5956 self.active_diagnostics.is_some()
5957 }
5958
5959 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
5960 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
5961 self.display_map.update(cx, |display_map, cx| {
5962 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
5963 });
5964 cx.notify();
5965 }
5966 }
5967
5968 pub fn set_selections_from_remote(
5969 &mut self,
5970 selections: Vec<Selection<Anchor>>,
5971 pending_selection: Option<Selection<Anchor>>,
5972 cx: &mut ViewContext<Self>,
5973 ) {
5974 let old_cursor_position = self.selections.newest_anchor().head();
5975 self.selections.change_with(cx, |s| {
5976 s.select_anchors(selections);
5977 if let Some(pending_selection) = pending_selection {
5978 s.set_pending(pending_selection, SelectMode::Character);
5979 } else {
5980 s.clear_pending();
5981 }
5982 });
5983 self.selections_did_change(false, &old_cursor_position, cx);
5984 }
5985
5986 fn push_to_selection_history(&mut self) {
5987 self.selection_history.push(SelectionHistoryEntry {
5988 selections: self.selections.disjoint_anchors(),
5989 select_next_state: self.select_next_state.clone(),
5990 add_selections_state: self.add_selections_state.clone(),
5991 });
5992 }
5993
5994 pub fn transact(
5995 &mut self,
5996 cx: &mut ViewContext<Self>,
5997 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
5998 ) -> Option<TransactionId> {
5999 self.start_transaction_at(Instant::now(), cx);
6000 update(self, cx);
6001 self.end_transaction_at(Instant::now(), cx)
6002 }
6003
6004 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
6005 self.end_selection(cx);
6006 if let Some(tx_id) = self
6007 .buffer
6008 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
6009 {
6010 self.selection_history
6011 .insert_transaction(tx_id, self.selections.disjoint_anchors());
6012 }
6013 }
6014
6015 fn end_transaction_at(
6016 &mut self,
6017 now: Instant,
6018 cx: &mut ViewContext<Self>,
6019 ) -> Option<TransactionId> {
6020 if let Some(tx_id) = self
6021 .buffer
6022 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
6023 {
6024 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
6025 *end_selections = Some(self.selections.disjoint_anchors());
6026 } else {
6027 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
6028 }
6029
6030 cx.emit(Event::Edited);
6031 Some(tx_id)
6032 } else {
6033 None
6034 }
6035 }
6036
6037 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
6038 let mut fold_ranges = Vec::new();
6039
6040 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6041
6042 let selections = self.selections.all::<Point>(cx);
6043 for selection in selections {
6044 let range = selection.range().sorted();
6045 let buffer_start_row = range.start.row;
6046
6047 for row in (0..=range.end.row).rev() {
6048 let fold_range = display_map.foldable_range(row);
6049
6050 if let Some(fold_range) = fold_range {
6051 if fold_range.end.row >= buffer_start_row {
6052 fold_ranges.push(fold_range);
6053 if row <= range.start.row {
6054 break;
6055 }
6056 }
6057 }
6058 }
6059 }
6060
6061 self.fold_ranges(fold_ranges, true, cx);
6062 }
6063
6064 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
6065 let buffer_row = fold_at.buffer_row;
6066 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6067
6068 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
6069 let autoscroll = self
6070 .selections
6071 .all::<Point>(cx)
6072 .iter()
6073 .any(|selection| fold_range.overlaps(&selection.range()));
6074
6075 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
6076 }
6077 }
6078
6079 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
6080 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6081 let buffer = &display_map.buffer_snapshot;
6082 let selections = self.selections.all::<Point>(cx);
6083 let ranges = selections
6084 .iter()
6085 .map(|s| {
6086 let range = s.display_range(&display_map).sorted();
6087 let mut start = range.start.to_point(&display_map);
6088 let mut end = range.end.to_point(&display_map);
6089 start.column = 0;
6090 end.column = buffer.line_len(end.row);
6091 start..end
6092 })
6093 .collect::<Vec<_>>();
6094
6095 self.unfold_ranges(ranges, true, true, cx);
6096 }
6097
6098 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
6099 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6100
6101 let intersection_range = Point::new(unfold_at.buffer_row, 0)
6102 ..Point::new(
6103 unfold_at.buffer_row,
6104 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
6105 );
6106
6107 let autoscroll = self
6108 .selections
6109 .all::<Point>(cx)
6110 .iter()
6111 .any(|selection| selection.range().overlaps(&intersection_range));
6112
6113 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
6114 }
6115
6116 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
6117 let selections = self.selections.all::<Point>(cx);
6118 let ranges = selections.into_iter().map(|s| s.start..s.end);
6119 self.fold_ranges(ranges, true, cx);
6120 }
6121
6122 pub fn fold_ranges<T: ToOffset + Clone>(
6123 &mut self,
6124 ranges: impl IntoIterator<Item = Range<T>>,
6125 auto_scroll: bool,
6126 cx: &mut ViewContext<Self>,
6127 ) {
6128 let mut ranges = ranges.into_iter().peekable();
6129 if ranges.peek().is_some() {
6130 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
6131
6132 if auto_scroll {
6133 self.request_autoscroll(Autoscroll::fit(), cx);
6134 }
6135
6136 cx.notify();
6137 }
6138 }
6139
6140 pub fn unfold_ranges<T: ToOffset + Clone>(
6141 &mut self,
6142 ranges: impl IntoIterator<Item = Range<T>>,
6143 inclusive: bool,
6144 auto_scroll: bool,
6145 cx: &mut ViewContext<Self>,
6146 ) {
6147 let mut ranges = ranges.into_iter().peekable();
6148 if ranges.peek().is_some() {
6149 self.display_map
6150 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
6151 if auto_scroll {
6152 self.request_autoscroll(Autoscroll::fit(), cx);
6153 }
6154
6155 cx.notify();
6156 }
6157 }
6158
6159 pub fn gutter_hover(
6160 &mut self,
6161 GutterHover { hovered }: &GutterHover,
6162 cx: &mut ViewContext<Self>,
6163 ) {
6164 self.gutter_hovered = *hovered;
6165 cx.notify();
6166 }
6167
6168 pub fn insert_blocks(
6169 &mut self,
6170 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
6171 cx: &mut ViewContext<Self>,
6172 ) -> Vec<BlockId> {
6173 let blocks = self
6174 .display_map
6175 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
6176 self.request_autoscroll(Autoscroll::fit(), cx);
6177 blocks
6178 }
6179
6180 pub fn replace_blocks(
6181 &mut self,
6182 blocks: HashMap<BlockId, RenderBlock>,
6183 cx: &mut ViewContext<Self>,
6184 ) {
6185 self.display_map
6186 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
6187 self.request_autoscroll(Autoscroll::fit(), cx);
6188 }
6189
6190 pub fn remove_blocks(&mut self, block_ids: HashSet<BlockId>, cx: &mut ViewContext<Self>) {
6191 self.display_map.update(cx, |display_map, cx| {
6192 display_map.remove_blocks(block_ids, cx)
6193 });
6194 }
6195
6196 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
6197 self.display_map
6198 .update(cx, |map, cx| map.snapshot(cx))
6199 .longest_row()
6200 }
6201
6202 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
6203 self.display_map
6204 .update(cx, |map, cx| map.snapshot(cx))
6205 .max_point()
6206 }
6207
6208 pub fn text(&self, cx: &AppContext) -> String {
6209 self.buffer.read(cx).read(cx).text()
6210 }
6211
6212 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
6213 self.transact(cx, |this, cx| {
6214 this.buffer
6215 .read(cx)
6216 .as_singleton()
6217 .expect("you can only call set_text on editors for singleton buffers")
6218 .update(cx, |buffer, cx| buffer.set_text(text, cx));
6219 });
6220 }
6221
6222 pub fn display_text(&self, cx: &mut AppContext) -> String {
6223 self.display_map
6224 .update(cx, |map, cx| map.snapshot(cx))
6225 .text()
6226 }
6227
6228 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
6229 let language_name = self
6230 .buffer
6231 .read(cx)
6232 .as_singleton()
6233 .and_then(|singleton_buffer| singleton_buffer.read(cx).language())
6234 .map(|l| l.name());
6235
6236 let settings = cx.global::<Settings>();
6237 let mode = self
6238 .soft_wrap_mode_override
6239 .unwrap_or_else(|| settings.soft_wrap(language_name.as_deref()));
6240 match mode {
6241 settings::SoftWrap::None => SoftWrap::None,
6242 settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
6243 settings::SoftWrap::PreferredLineLength => {
6244 SoftWrap::Column(settings.preferred_line_length(language_name.as_deref()))
6245 }
6246 }
6247 }
6248
6249 pub fn set_soft_wrap_mode(&mut self, mode: settings::SoftWrap, cx: &mut ViewContext<Self>) {
6250 self.soft_wrap_mode_override = Some(mode);
6251 cx.notify();
6252 }
6253
6254 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut AppContext) -> bool {
6255 self.display_map
6256 .update(cx, |map, cx| map.set_wrap_width(width, cx))
6257 }
6258
6259 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
6260 if self.soft_wrap_mode_override.is_some() {
6261 self.soft_wrap_mode_override.take();
6262 } else {
6263 let soft_wrap = match self.soft_wrap_mode(cx) {
6264 SoftWrap::None => settings::SoftWrap::EditorWidth,
6265 SoftWrap::EditorWidth | SoftWrap::Column(_) => settings::SoftWrap::None,
6266 };
6267 self.soft_wrap_mode_override = Some(soft_wrap);
6268 }
6269 cx.notify();
6270 }
6271
6272 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
6273 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6274 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6275 cx.reveal_path(&file.abs_path(cx));
6276 }
6277 }
6278 }
6279
6280 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
6281 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6282 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6283 if let Some(path) = file.abs_path(cx).to_str() {
6284 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
6285 }
6286 }
6287 }
6288 }
6289
6290 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
6291 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6292 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6293 if let Some(path) = file.path().to_str() {
6294 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
6295 }
6296 }
6297 }
6298 }
6299
6300 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
6301 self.highlighted_rows = rows;
6302 }
6303
6304 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
6305 self.highlighted_rows.clone()
6306 }
6307
6308 pub fn highlight_background<T: 'static>(
6309 &mut self,
6310 ranges: Vec<Range<Anchor>>,
6311 color_fetcher: fn(&Theme) -> Color,
6312 cx: &mut ViewContext<Self>,
6313 ) {
6314 self.background_highlights
6315 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
6316 cx.notify();
6317 }
6318
6319 #[allow(clippy::type_complexity)]
6320 pub fn clear_background_highlights<T: 'static>(
6321 &mut self,
6322 cx: &mut ViewContext<Self>,
6323 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
6324 let highlights = self.background_highlights.remove(&TypeId::of::<T>());
6325 if highlights.is_some() {
6326 cx.notify();
6327 }
6328 highlights
6329 }
6330
6331 #[cfg(feature = "test-support")]
6332 pub fn all_background_highlights(
6333 &mut self,
6334 cx: &mut ViewContext<Self>,
6335 ) -> Vec<(Range<DisplayPoint>, Color)> {
6336 let snapshot = self.snapshot(cx);
6337 let buffer = &snapshot.buffer_snapshot;
6338 let start = buffer.anchor_before(0);
6339 let end = buffer.anchor_after(buffer.len());
6340 let theme = cx.global::<Settings>().theme.as_ref();
6341 self.background_highlights_in_range(start..end, &snapshot, theme)
6342 }
6343
6344 fn document_highlights_for_position<'a>(
6345 &'a self,
6346 position: Anchor,
6347 buffer: &'a MultiBufferSnapshot,
6348 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
6349 let read_highlights = self
6350 .background_highlights
6351 .get(&TypeId::of::<DocumentHighlightRead>())
6352 .map(|h| &h.1);
6353 let write_highlights = self
6354 .background_highlights
6355 .get(&TypeId::of::<DocumentHighlightWrite>())
6356 .map(|h| &h.1);
6357 let left_position = position.bias_left(buffer);
6358 let right_position = position.bias_right(buffer);
6359 read_highlights
6360 .into_iter()
6361 .chain(write_highlights)
6362 .flat_map(move |ranges| {
6363 let start_ix = match ranges.binary_search_by(|probe| {
6364 let cmp = probe.end.cmp(&left_position, buffer);
6365 if cmp.is_ge() {
6366 Ordering::Greater
6367 } else {
6368 Ordering::Less
6369 }
6370 }) {
6371 Ok(i) | Err(i) => i,
6372 };
6373
6374 let right_position = right_position.clone();
6375 ranges[start_ix..]
6376 .iter()
6377 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
6378 })
6379 }
6380
6381 pub fn background_highlights_in_range(
6382 &self,
6383 search_range: Range<Anchor>,
6384 display_snapshot: &DisplaySnapshot,
6385 theme: &Theme,
6386 ) -> Vec<(Range<DisplayPoint>, Color)> {
6387 let mut results = Vec::new();
6388 let buffer = &display_snapshot.buffer_snapshot;
6389 for (color_fetcher, ranges) in self.background_highlights.values() {
6390 let color = color_fetcher(theme);
6391 let start_ix = match ranges.binary_search_by(|probe| {
6392 let cmp = probe.end.cmp(&search_range.start, buffer);
6393 if cmp.is_gt() {
6394 Ordering::Greater
6395 } else {
6396 Ordering::Less
6397 }
6398 }) {
6399 Ok(i) | Err(i) => i,
6400 };
6401 for range in &ranges[start_ix..] {
6402 if range.start.cmp(&search_range.end, buffer).is_ge() {
6403 break;
6404 }
6405 let start = range
6406 .start
6407 .to_point(buffer)
6408 .to_display_point(display_snapshot);
6409 let end = range
6410 .end
6411 .to_point(buffer)
6412 .to_display_point(display_snapshot);
6413 results.push((start..end, color))
6414 }
6415 }
6416 results
6417 }
6418
6419 pub fn highlight_text<T: 'static>(
6420 &mut self,
6421 ranges: Vec<Range<Anchor>>,
6422 style: HighlightStyle,
6423 cx: &mut ViewContext<Self>,
6424 ) {
6425 self.display_map.update(cx, |map, _| {
6426 map.highlight_text(TypeId::of::<T>(), ranges, style)
6427 });
6428 cx.notify();
6429 }
6430
6431 pub fn text_highlights<'a, T: 'static>(
6432 &'a self,
6433 cx: &'a AppContext,
6434 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
6435 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
6436 }
6437
6438 pub fn clear_text_highlights<T: 'static>(
6439 &mut self,
6440 cx: &mut ViewContext<Self>,
6441 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
6442 let highlights = self
6443 .display_map
6444 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()));
6445 if highlights.is_some() {
6446 cx.notify();
6447 }
6448 highlights
6449 }
6450
6451 pub fn show_local_cursors(&self, cx: &AppContext) -> bool {
6452 self.blink_manager.read(cx).visible() && self.focused
6453 }
6454
6455 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
6456 cx.notify();
6457 }
6458
6459 fn on_buffer_event(
6460 &mut self,
6461 _: ModelHandle<MultiBuffer>,
6462 event: &multi_buffer::Event,
6463 cx: &mut ViewContext<Self>,
6464 ) {
6465 match event {
6466 multi_buffer::Event::Edited => {
6467 self.refresh_active_diagnostics(cx);
6468 self.refresh_code_actions(cx);
6469 if self.has_active_copilot_suggestion(cx) {
6470 self.update_visible_copilot_suggestion(cx);
6471 }
6472 cx.emit(Event::BufferEdited);
6473 }
6474 multi_buffer::Event::ExcerptsAdded {
6475 buffer,
6476 predecessor,
6477 excerpts,
6478 } => cx.emit(Event::ExcerptsAdded {
6479 buffer: buffer.clone(),
6480 predecessor: *predecessor,
6481 excerpts: excerpts.clone(),
6482 }),
6483 multi_buffer::Event::ExcerptsRemoved { ids } => {
6484 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
6485 }
6486 multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed),
6487 multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
6488 multi_buffer::Event::Saved => cx.emit(Event::Saved),
6489 multi_buffer::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
6490 multi_buffer::Event::Reloaded => cx.emit(Event::TitleChanged),
6491 multi_buffer::Event::Closed => cx.emit(Event::Closed),
6492 multi_buffer::Event::DiagnosticsUpdated => {
6493 self.refresh_active_diagnostics(cx);
6494 }
6495 }
6496 }
6497
6498 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
6499 cx.notify();
6500 }
6501
6502 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
6503 self.refresh_copilot_suggestions(cx);
6504 }
6505
6506 pub fn set_searchable(&mut self, searchable: bool) {
6507 self.searchable = searchable;
6508 }
6509
6510 pub fn searchable(&self) -> bool {
6511 self.searchable
6512 }
6513
6514 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
6515 let active_item = workspace.active_item(cx);
6516 let editor_handle = if let Some(editor) = active_item
6517 .as_ref()
6518 .and_then(|item| item.act_as::<Self>(cx))
6519 {
6520 editor
6521 } else {
6522 cx.propagate_action();
6523 return;
6524 };
6525
6526 let editor = editor_handle.read(cx);
6527 let buffer = editor.buffer.read(cx);
6528 if buffer.is_singleton() {
6529 cx.propagate_action();
6530 return;
6531 }
6532
6533 let mut new_selections_by_buffer = HashMap::default();
6534 for selection in editor.selections.all::<usize>(cx) {
6535 for (buffer, mut range) in
6536 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
6537 {
6538 if selection.reversed {
6539 mem::swap(&mut range.start, &mut range.end);
6540 }
6541 new_selections_by_buffer
6542 .entry(buffer)
6543 .or_insert(Vec::new())
6544 .push(range)
6545 }
6546 }
6547
6548 editor_handle.update(cx, |editor, cx| {
6549 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
6550 });
6551 let pane = workspace.active_pane().clone();
6552 pane.update(cx, |pane, _| pane.disable_history());
6553
6554 // We defer the pane interaction because we ourselves are a workspace item
6555 // and activating a new item causes the pane to call a method on us reentrantly,
6556 // which panics if we're on the stack.
6557 cx.defer(move |workspace, cx| {
6558 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
6559 let editor = workspace.open_project_item::<Self>(buffer, cx);
6560 editor.update(cx, |editor, cx| {
6561 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
6562 s.select_ranges(ranges);
6563 });
6564 });
6565 }
6566
6567 pane.update(cx, |pane, _| pane.enable_history());
6568 });
6569 }
6570
6571 fn jump(workspace: &mut Workspace, action: &Jump, cx: &mut ViewContext<Workspace>) {
6572 let editor = workspace.open_path(action.path.clone(), None, true, cx);
6573 let position = action.position;
6574 let anchor = action.anchor;
6575 cx.spawn_weak(|_, mut cx| async move {
6576 let editor = editor.await.log_err()?.downcast::<Editor>()?;
6577 editor.update(&mut cx, |editor, cx| {
6578 let buffer = editor.buffer().read(cx).as_singleton()?;
6579 let buffer = buffer.read(cx);
6580 let cursor = if buffer.can_resolve(&anchor) {
6581 language::ToPoint::to_point(&anchor, buffer)
6582 } else {
6583 buffer.clip_point(position, Bias::Left)
6584 };
6585
6586 let nav_history = editor.nav_history.take();
6587 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
6588 s.select_ranges([cursor..cursor]);
6589 });
6590 editor.nav_history = nav_history;
6591
6592 Some(())
6593 })?;
6594 Some(())
6595 })
6596 .detach()
6597 }
6598
6599 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
6600 let snapshot = self.buffer.read(cx).read(cx);
6601 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
6602 Some(
6603 ranges
6604 .iter()
6605 .map(move |range| {
6606 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
6607 })
6608 .collect(),
6609 )
6610 }
6611
6612 fn selection_replacement_ranges(
6613 &self,
6614 range: Range<OffsetUtf16>,
6615 cx: &AppContext,
6616 ) -> Vec<Range<OffsetUtf16>> {
6617 let selections = self.selections.all::<OffsetUtf16>(cx);
6618 let newest_selection = selections
6619 .iter()
6620 .max_by_key(|selection| selection.id)
6621 .unwrap();
6622 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
6623 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
6624 let snapshot = self.buffer.read(cx).read(cx);
6625 selections
6626 .into_iter()
6627 .map(|mut selection| {
6628 selection.start.0 =
6629 (selection.start.0 as isize).saturating_add(start_delta) as usize;
6630 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
6631 snapshot.clip_offset_utf16(selection.start, Bias::Left)
6632 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
6633 })
6634 .collect()
6635 }
6636
6637 fn report_event(&self, name: &str, cx: &AppContext) {
6638 if let Some((project, file)) = self.project.as_ref().zip(
6639 self.buffer
6640 .read(cx)
6641 .as_singleton()
6642 .and_then(|b| b.read(cx).file()),
6643 ) {
6644 let settings = cx.global::<Settings>();
6645
6646 let extension = Path::new(file.file_name(cx))
6647 .extension()
6648 .and_then(|e| e.to_str());
6649 project.read(cx).client().report_event(
6650 name,
6651 json!({ "File Extension": extension, "Vim Mode": settings.vim_mode }),
6652 settings.telemetry(),
6653 );
6654 }
6655 }
6656
6657 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
6658 /// with each line being an array of {text, highlight} objects.
6659 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
6660 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
6661 return;
6662 };
6663
6664 #[derive(Serialize)]
6665 struct Chunk<'a> {
6666 text: String,
6667 highlight: Option<&'a str>,
6668 }
6669
6670 let snapshot = buffer.read(cx).snapshot();
6671 let range = self
6672 .selected_text_range(cx)
6673 .and_then(|selected_range| {
6674 if selected_range.is_empty() {
6675 None
6676 } else {
6677 Some(selected_range)
6678 }
6679 })
6680 .unwrap_or_else(|| 0..snapshot.len());
6681
6682 let chunks = snapshot.chunks(range, true);
6683 let mut lines = Vec::new();
6684 let mut line: VecDeque<Chunk> = VecDeque::new();
6685
6686 let theme = &cx.global::<Settings>().theme.editor.syntax;
6687
6688 for chunk in chunks {
6689 let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme));
6690 let mut chunk_lines = chunk.text.split("\n").peekable();
6691 while let Some(text) = chunk_lines.next() {
6692 let mut merged_with_last_token = false;
6693 if let Some(last_token) = line.back_mut() {
6694 if last_token.highlight == highlight {
6695 last_token.text.push_str(text);
6696 merged_with_last_token = true;
6697 }
6698 }
6699
6700 if !merged_with_last_token {
6701 line.push_back(Chunk {
6702 text: text.into(),
6703 highlight,
6704 });
6705 }
6706
6707 if chunk_lines.peek().is_some() {
6708 if line.len() > 1 && line.front().unwrap().text.is_empty() {
6709 line.pop_front();
6710 }
6711 if line.len() > 1 && line.back().unwrap().text.is_empty() {
6712 line.pop_back();
6713 }
6714
6715 lines.push(mem::take(&mut line));
6716 }
6717 }
6718 }
6719
6720 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else { return; };
6721 cx.write_to_clipboard(ClipboardItem::new(lines));
6722 }
6723}
6724
6725fn consume_contiguous_rows(
6726 contiguous_row_selections: &mut Vec<Selection<Point>>,
6727 selection: &Selection<Point>,
6728 display_map: &DisplaySnapshot,
6729 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
6730) -> (u32, u32) {
6731 contiguous_row_selections.push(selection.clone());
6732 let start_row = selection.start.row;
6733 let mut end_row = ending_row(selection, display_map);
6734
6735 while let Some(next_selection) = selections.peek() {
6736 if next_selection.start.row <= end_row {
6737 end_row = ending_row(next_selection, display_map);
6738 contiguous_row_selections.push(selections.next().unwrap().clone());
6739 } else {
6740 break;
6741 }
6742 }
6743 (start_row, end_row)
6744}
6745
6746fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
6747 if next_selection.end.column > 0 || next_selection.is_empty() {
6748 display_map.next_line_boundary(next_selection.end).0.row + 1
6749 } else {
6750 next_selection.end.row
6751 }
6752}
6753
6754impl EditorSnapshot {
6755 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
6756 self.display_snapshot.buffer_snapshot.language_at(position)
6757 }
6758
6759 pub fn is_focused(&self) -> bool {
6760 self.is_focused
6761 }
6762
6763 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
6764 self.placeholder_text.as_ref()
6765 }
6766
6767 pub fn scroll_position(&self) -> Vector2F {
6768 self.scroll_anchor.scroll_position(&self.display_snapshot)
6769 }
6770}
6771
6772impl Deref for EditorSnapshot {
6773 type Target = DisplaySnapshot;
6774
6775 fn deref(&self) -> &Self::Target {
6776 &self.display_snapshot
6777 }
6778}
6779
6780#[derive(Clone, Debug, PartialEq, Eq)]
6781pub enum Event {
6782 InputIgnored {
6783 text: Arc<str>,
6784 },
6785 ExcerptsAdded {
6786 buffer: ModelHandle<Buffer>,
6787 predecessor: ExcerptId,
6788 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
6789 },
6790 ExcerptsRemoved {
6791 ids: Vec<ExcerptId>,
6792 },
6793 BufferEdited,
6794 Edited,
6795 Reparsed,
6796 Blurred,
6797 DirtyChanged,
6798 Saved,
6799 TitleChanged,
6800 SelectionsChanged {
6801 local: bool,
6802 },
6803 ScrollPositionChanged {
6804 local: bool,
6805 },
6806 Closed,
6807}
6808
6809pub struct EditorFocused(pub ViewHandle<Editor>);
6810pub struct EditorBlurred(pub ViewHandle<Editor>);
6811pub struct EditorReleased(pub WeakViewHandle<Editor>);
6812
6813impl Entity for Editor {
6814 type Event = Event;
6815
6816 fn release(&mut self, cx: &mut AppContext) {
6817 cx.emit_global(EditorReleased(self.handle.clone()));
6818 }
6819}
6820
6821impl View for Editor {
6822 fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
6823 let style = self.style(cx);
6824 let font_changed = self.display_map.update(cx, |map, cx| {
6825 map.set_fold_ellipses_color(style.folds.ellipses.text_color);
6826 map.set_font(style.text.font_id, style.text.font_size, cx)
6827 });
6828
6829 if font_changed {
6830 let handle = self.handle.clone();
6831 cx.defer(move |cx| {
6832 if let Some(editor) = handle.upgrade(cx) {
6833 editor.update(cx, |editor, cx| {
6834 hide_hover(editor, &HideHover, cx);
6835 hide_link_definition(editor, cx);
6836 })
6837 }
6838 });
6839 }
6840
6841 Stack::new()
6842 .with_child(EditorElement::new(self.handle.clone(), 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}