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::on_settings_changed),
1269 ],
1270 };
1271 this.end_selection(cx);
1272 this.scroll_manager.show_scrollbar(cx);
1273
1274 let editor_created_event = EditorCreated(cx.handle());
1275 cx.emit_global(editor_created_event);
1276
1277 if mode == EditorMode::Full {
1278 let should_auto_hide_scrollbars = cx.platform().should_auto_hide_scrollbars();
1279 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1280 }
1281
1282 this.report_event("open editor", cx);
1283 this
1284 }
1285
1286 pub fn new_file(
1287 workspace: &mut Workspace,
1288 _: &workspace::NewFile,
1289 cx: &mut ViewContext<Workspace>,
1290 ) {
1291 let project = workspace.project().clone();
1292 if project.read(cx).is_remote() {
1293 cx.propagate_action();
1294 } else if let Some(buffer) = project
1295 .update(cx, |project, cx| project.create_buffer("", None, cx))
1296 .log_err()
1297 {
1298 workspace.add_item(
1299 Box::new(cx.add_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx))),
1300 cx,
1301 );
1302 }
1303 }
1304
1305 pub fn replica_id(&self, cx: &AppContext) -> ReplicaId {
1306 self.buffer.read(cx).replica_id()
1307 }
1308
1309 pub fn leader_replica_id(&self) -> Option<ReplicaId> {
1310 self.leader_replica_id
1311 }
1312
1313 pub fn buffer(&self) -> &ModelHandle<MultiBuffer> {
1314 &self.buffer
1315 }
1316
1317 pub fn title<'a>(&self, cx: &'a AppContext) -> Cow<'a, str> {
1318 self.buffer().read(cx).title(cx)
1319 }
1320
1321 pub fn snapshot(&mut self, cx: &mut 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.hide_copilot_suggestion(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.global::<Settings>().copilot_on(language_name.as_deref()) {
2820 self.hide_copilot_suggestion(cx);
2821 return None;
2822 }
2823
2824 let (buffer, buffer_position) =
2825 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
2826 self.copilot_state.pending_refresh = cx.spawn_weak(|this, mut cx| async move {
2827 cx.background().timer(COPILOT_DEBOUNCE_TIMEOUT).await;
2828 let (completion, completions_cycling) = copilot.update(&mut cx, |copilot, cx| {
2829 (
2830 copilot.completions(&buffer, buffer_position, cx),
2831 copilot.completions_cycling(&buffer, buffer_position, cx),
2832 )
2833 });
2834
2835 let (completion, completions_cycling) = futures::join!(completion, completions_cycling);
2836 let mut completions = Vec::new();
2837 completions.extend(completion.log_err().into_iter().flatten());
2838 completions.extend(completions_cycling.log_err().into_iter().flatten());
2839 this.upgrade(&cx)?.update(&mut cx, |this, cx| {
2840 if !completions.is_empty() {
2841 this.copilot_state.completions.clear();
2842 this.copilot_state.active_completion_index = 0;
2843 this.copilot_state.excerpt_id = Some(cursor.excerpt_id);
2844 for completion in completions {
2845 this.copilot_state.push_completion(completion);
2846 }
2847 this.update_visible_copilot_suggestion(cx);
2848 }
2849 });
2850
2851 Some(())
2852 });
2853
2854 Some(())
2855 }
2856
2857 fn next_copilot_suggestion(&mut self, _: &copilot::NextSuggestion, cx: &mut ViewContext<Self>) {
2858 if !self.has_active_copilot_suggestion(cx) {
2859 self.refresh_copilot_suggestions(cx);
2860 return;
2861 }
2862
2863 self.copilot_state.active_completion_index =
2864 (self.copilot_state.active_completion_index + 1) % self.copilot_state.completions.len();
2865 self.update_visible_copilot_suggestion(cx);
2866 }
2867
2868 fn previous_copilot_suggestion(
2869 &mut self,
2870 _: &copilot::PreviousSuggestion,
2871 cx: &mut ViewContext<Self>,
2872 ) {
2873 if !self.has_active_copilot_suggestion(cx) {
2874 self.refresh_copilot_suggestions(cx);
2875 return;
2876 }
2877
2878 self.copilot_state.active_completion_index =
2879 if self.copilot_state.active_completion_index == 0 {
2880 self.copilot_state.completions.len() - 1
2881 } else {
2882 self.copilot_state.active_completion_index - 1
2883 };
2884 self.update_visible_copilot_suggestion(cx);
2885 }
2886
2887 fn accept_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
2888 if let Some(text) = self.hide_copilot_suggestion(cx) {
2889 self.insert_with_autoindent_mode(&text.to_string(), None, cx);
2890 true
2891 } else {
2892 false
2893 }
2894 }
2895
2896 fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool {
2897 self.display_map.read(cx).has_suggestion()
2898 }
2899
2900 fn hide_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> Option<Rope> {
2901 if self.has_active_copilot_suggestion(cx) {
2902 let old_suggestion = self
2903 .display_map
2904 .update(cx, |map, cx| map.replace_suggestion::<usize>(None, cx));
2905 cx.notify();
2906 old_suggestion.map(|suggestion| suggestion.text)
2907 } else {
2908 None
2909 }
2910 }
2911
2912 fn update_visible_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) {
2913 let snapshot = self.buffer.read(cx).snapshot(cx);
2914 let selection = self.selections.newest_anchor();
2915 let cursor = selection.head();
2916
2917 if self.context_menu.is_some()
2918 || !self.completion_tasks.is_empty()
2919 || selection.start != selection.end
2920 {
2921 self.hide_copilot_suggestion(cx);
2922 } else if let Some(text) = self
2923 .copilot_state
2924 .text_for_active_completion(cursor, &snapshot)
2925 {
2926 self.display_map.update(cx, |map, cx| {
2927 map.replace_suggestion(
2928 Some(Suggestion {
2929 position: cursor,
2930 text: text.into(),
2931 }),
2932 cx,
2933 )
2934 });
2935 cx.notify();
2936 } else {
2937 self.hide_copilot_suggestion(cx);
2938 }
2939 }
2940
2941 pub fn render_code_actions_indicator(
2942 &self,
2943 style: &EditorStyle,
2944 active: bool,
2945 cx: &mut RenderContext<Self>,
2946 ) -> Option<ElementBox> {
2947 if self.available_code_actions.is_some() {
2948 enum CodeActions {}
2949 Some(
2950 MouseEventHandler::<CodeActions>::new(0, cx, |state, _| {
2951 Svg::new("icons/bolt_8.svg")
2952 .with_color(style.code_actions.indicator.style_for(state, active).color)
2953 .boxed()
2954 })
2955 .with_cursor_style(CursorStyle::PointingHand)
2956 .with_padding(Padding::uniform(3.))
2957 .on_down(MouseButton::Left, |_, cx| {
2958 cx.dispatch_action(ToggleCodeActions {
2959 deployed_from_indicator: true,
2960 });
2961 })
2962 .boxed(),
2963 )
2964 } else {
2965 None
2966 }
2967 }
2968
2969 pub fn render_fold_indicators(
2970 &self,
2971 fold_data: Vec<Option<(FoldStatus, u32, bool)>>,
2972 style: &EditorStyle,
2973 gutter_hovered: bool,
2974 line_height: f32,
2975 gutter_margin: f32,
2976 cx: &mut RenderContext<Self>,
2977 ) -> Vec<Option<ElementBox>> {
2978 enum FoldIndicators {}
2979
2980 let style = style.folds.clone();
2981
2982 fold_data
2983 .iter()
2984 .enumerate()
2985 .map(|(ix, fold_data)| {
2986 fold_data
2987 .map(|(fold_status, buffer_row, active)| {
2988 (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| {
2989 MouseEventHandler::<FoldIndicators>::new(
2990 ix as usize,
2991 cx,
2992 |mouse_state, _| -> ElementBox {
2993 Svg::new(match fold_status {
2994 FoldStatus::Folded => style.folded_icon.clone(),
2995 FoldStatus::Foldable => style.foldable_icon.clone(),
2996 })
2997 .with_color(
2998 style
2999 .indicator
3000 .style_for(
3001 mouse_state,
3002 fold_status == FoldStatus::Folded,
3003 )
3004 .color,
3005 )
3006 .constrained()
3007 .with_width(gutter_margin * style.icon_margin_scale)
3008 .aligned()
3009 .constrained()
3010 .with_height(line_height)
3011 .with_width(gutter_margin)
3012 .aligned()
3013 .boxed()
3014 },
3015 )
3016 .with_cursor_style(CursorStyle::PointingHand)
3017 .with_padding(Padding::uniform(3.))
3018 .on_click(MouseButton::Left, {
3019 move |_, cx| {
3020 cx.dispatch_any_action(match fold_status {
3021 FoldStatus::Folded => Box::new(UnfoldAt { buffer_row }),
3022 FoldStatus::Foldable => Box::new(FoldAt { buffer_row }),
3023 });
3024 }
3025 })
3026 .boxed()
3027 })
3028 })
3029 .flatten()
3030 })
3031 .collect()
3032 }
3033
3034 pub fn context_menu_visible(&self) -> bool {
3035 self.context_menu
3036 .as_ref()
3037 .map_or(false, |menu| menu.visible())
3038 }
3039
3040 pub fn render_context_menu(
3041 &self,
3042 cursor_position: DisplayPoint,
3043 style: EditorStyle,
3044 cx: &mut RenderContext<Editor>,
3045 ) -> Option<(DisplayPoint, ElementBox)> {
3046 self.context_menu
3047 .as_ref()
3048 .map(|menu| menu.render(cursor_position, style, cx))
3049 }
3050
3051 fn show_context_menu(&mut self, menu: ContextMenu, cx: &mut ViewContext<Self>) {
3052 if !matches!(menu, ContextMenu::Completions(_)) {
3053 self.completion_tasks.clear();
3054 }
3055 self.context_menu = Some(menu);
3056 self.hide_copilot_suggestion(cx);
3057 cx.notify();
3058 }
3059
3060 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
3061 cx.notify();
3062 self.completion_tasks.clear();
3063 let context_menu = self.context_menu.take();
3064 if context_menu.is_some() {
3065 self.update_visible_copilot_suggestion(cx);
3066 }
3067 context_menu
3068 }
3069
3070 pub fn insert_snippet(
3071 &mut self,
3072 insertion_ranges: &[Range<usize>],
3073 snippet: Snippet,
3074 cx: &mut ViewContext<Self>,
3075 ) -> Result<()> {
3076 let tabstops = self.buffer.update(cx, |buffer, cx| {
3077 let snippet_text: Arc<str> = snippet.text.clone().into();
3078 buffer.edit(
3079 insertion_ranges
3080 .iter()
3081 .cloned()
3082 .map(|range| (range, snippet_text.clone())),
3083 Some(AutoindentMode::EachLine),
3084 cx,
3085 );
3086
3087 let snapshot = &*buffer.read(cx);
3088 let snippet = &snippet;
3089 snippet
3090 .tabstops
3091 .iter()
3092 .map(|tabstop| {
3093 let mut tabstop_ranges = tabstop
3094 .iter()
3095 .flat_map(|tabstop_range| {
3096 let mut delta = 0_isize;
3097 insertion_ranges.iter().map(move |insertion_range| {
3098 let insertion_start = insertion_range.start as isize + delta;
3099 delta +=
3100 snippet.text.len() as isize - insertion_range.len() as isize;
3101
3102 let start = snapshot.anchor_before(
3103 (insertion_start + tabstop_range.start) as usize,
3104 );
3105 let end = snapshot
3106 .anchor_after((insertion_start + tabstop_range.end) as usize);
3107 start..end
3108 })
3109 })
3110 .collect::<Vec<_>>();
3111 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
3112 tabstop_ranges
3113 })
3114 .collect::<Vec<_>>()
3115 });
3116
3117 if let Some(tabstop) = tabstops.first() {
3118 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
3119 s.select_ranges(tabstop.iter().cloned());
3120 });
3121 self.snippet_stack.push(SnippetState {
3122 active_index: 0,
3123 ranges: tabstops,
3124 });
3125 }
3126
3127 Ok(())
3128 }
3129
3130 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
3131 self.move_to_snippet_tabstop(Bias::Right, cx)
3132 }
3133
3134 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
3135 self.move_to_snippet_tabstop(Bias::Left, cx)
3136 }
3137
3138 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
3139 if let Some(mut snippet) = self.snippet_stack.pop() {
3140 match bias {
3141 Bias::Left => {
3142 if snippet.active_index > 0 {
3143 snippet.active_index -= 1;
3144 } else {
3145 self.snippet_stack.push(snippet);
3146 return false;
3147 }
3148 }
3149 Bias::Right => {
3150 if snippet.active_index + 1 < snippet.ranges.len() {
3151 snippet.active_index += 1;
3152 } else {
3153 self.snippet_stack.push(snippet);
3154 return false;
3155 }
3156 }
3157 }
3158 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
3159 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
3160 s.select_anchor_ranges(current_ranges.iter().cloned())
3161 });
3162 // If snippet state is not at the last tabstop, push it back on the stack
3163 if snippet.active_index + 1 < snippet.ranges.len() {
3164 self.snippet_stack.push(snippet);
3165 }
3166 return true;
3167 }
3168 }
3169
3170 false
3171 }
3172
3173 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
3174 self.transact(cx, |this, cx| {
3175 this.select_all(&SelectAll, cx);
3176 this.insert("", cx);
3177 });
3178 }
3179
3180 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
3181 self.transact(cx, |this, cx| {
3182 this.select_autoclose_pair(cx);
3183 let mut selections = this.selections.all::<Point>(cx);
3184 if !this.selections.line_mode {
3185 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
3186 for selection in &mut selections {
3187 if selection.is_empty() {
3188 let old_head = selection.head();
3189 let mut new_head =
3190 movement::left(&display_map, old_head.to_display_point(&display_map))
3191 .to_point(&display_map);
3192 if let Some((buffer, line_buffer_range)) = display_map
3193 .buffer_snapshot
3194 .buffer_line_for_row(old_head.row)
3195 {
3196 let indent_size =
3197 buffer.indent_size_for_line(line_buffer_range.start.row);
3198 let language_name = buffer
3199 .language_at(line_buffer_range.start)
3200 .map(|language| language.name());
3201 let indent_len = match indent_size.kind {
3202 IndentKind::Space => {
3203 cx.global::<Settings>().tab_size(language_name.as_deref())
3204 }
3205 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
3206 };
3207 if old_head.column <= indent_size.len && old_head.column > 0 {
3208 let indent_len = indent_len.get();
3209 new_head = cmp::min(
3210 new_head,
3211 Point::new(
3212 old_head.row,
3213 ((old_head.column - 1) / indent_len) * indent_len,
3214 ),
3215 );
3216 }
3217 }
3218
3219 selection.set_head(new_head, SelectionGoal::None);
3220 }
3221 }
3222 }
3223
3224 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3225 this.insert("", cx);
3226 this.refresh_copilot_suggestions(cx);
3227 });
3228 }
3229
3230 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
3231 self.transact(cx, |this, cx| {
3232 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3233 let line_mode = s.line_mode;
3234 s.move_with(|map, selection| {
3235 if selection.is_empty() && !line_mode {
3236 let cursor = movement::right(map, selection.head());
3237 selection.set_head(cursor, SelectionGoal::None);
3238 }
3239 })
3240 });
3241 this.insert("", cx);
3242 this.refresh_copilot_suggestions(cx);
3243 });
3244 }
3245
3246 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
3247 if self.move_to_prev_snippet_tabstop(cx) {
3248 return;
3249 }
3250
3251 self.outdent(&Outdent, cx);
3252 }
3253
3254 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
3255 if self.move_to_next_snippet_tabstop(cx) {
3256 return;
3257 }
3258
3259 let mut selections = self.selections.all_adjusted(cx);
3260 let buffer = self.buffer.read(cx);
3261 let snapshot = buffer.snapshot(cx);
3262 let rows_iter = selections.iter().map(|s| s.head().row);
3263 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
3264
3265 let mut edits = Vec::new();
3266 let mut prev_edited_row = 0;
3267 let mut row_delta = 0;
3268 for selection in &mut selections {
3269 if selection.start.row != prev_edited_row {
3270 row_delta = 0;
3271 }
3272 prev_edited_row = selection.end.row;
3273
3274 // If the selection is non-empty, then increase the indentation of the selected lines.
3275 if !selection.is_empty() {
3276 row_delta =
3277 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3278 continue;
3279 }
3280
3281 // If the selection is empty and the cursor is in the leading whitespace before the
3282 // suggested indentation, then auto-indent the line.
3283 let cursor = selection.head();
3284 let current_indent = snapshot.indent_size_for_line(cursor.row);
3285 if let Some(suggested_indent) = suggested_indents.get(&cursor.row).copied() {
3286 if cursor.column < suggested_indent.len
3287 && cursor.column <= current_indent.len
3288 && current_indent.len <= suggested_indent.len
3289 {
3290 selection.start = Point::new(cursor.row, suggested_indent.len);
3291 selection.end = selection.start;
3292 if row_delta == 0 {
3293 edits.extend(Buffer::edit_for_indent_size_adjustment(
3294 cursor.row,
3295 current_indent,
3296 suggested_indent,
3297 ));
3298 row_delta = suggested_indent.len - current_indent.len;
3299 }
3300 continue;
3301 }
3302 }
3303
3304 // Accept copilot suggestion if there is only one selection and the cursor is not
3305 // in the leading whitespace.
3306 if self.selections.count() == 1
3307 && cursor.column >= current_indent.len
3308 && self.has_active_copilot_suggestion(cx)
3309 {
3310 self.accept_copilot_suggestion(cx);
3311 return;
3312 }
3313
3314 // Otherwise, insert a hard or soft tab.
3315 let settings = cx.global::<Settings>();
3316 let language_name = buffer.language_at(cursor, cx).map(|l| l.name());
3317 let tab_size = if settings.hard_tabs(language_name.as_deref()) {
3318 IndentSize::tab()
3319 } else {
3320 let tab_size = settings.tab_size(language_name.as_deref()).get();
3321 let char_column = snapshot
3322 .text_for_range(Point::new(cursor.row, 0)..cursor)
3323 .flat_map(str::chars)
3324 .count()
3325 + row_delta as usize;
3326 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
3327 IndentSize::spaces(chars_to_next_tab_stop)
3328 };
3329 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
3330 selection.end = selection.start;
3331 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
3332 row_delta += tab_size.len;
3333 }
3334
3335 self.transact(cx, |this, cx| {
3336 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3337 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3338 this.refresh_copilot_suggestions(cx);
3339 });
3340 }
3341
3342 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
3343 let mut selections = self.selections.all::<Point>(cx);
3344 let mut prev_edited_row = 0;
3345 let mut row_delta = 0;
3346 let mut edits = Vec::new();
3347 let buffer = self.buffer.read(cx);
3348 let snapshot = buffer.snapshot(cx);
3349 for selection in &mut selections {
3350 if selection.start.row != prev_edited_row {
3351 row_delta = 0;
3352 }
3353 prev_edited_row = selection.end.row;
3354
3355 row_delta =
3356 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3357 }
3358
3359 self.transact(cx, |this, cx| {
3360 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3361 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3362 });
3363 }
3364
3365 fn indent_selection(
3366 buffer: &MultiBuffer,
3367 snapshot: &MultiBufferSnapshot,
3368 selection: &mut Selection<Point>,
3369 edits: &mut Vec<(Range<Point>, String)>,
3370 delta_for_start_row: u32,
3371 cx: &AppContext,
3372 ) -> u32 {
3373 let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
3374 let settings = cx.global::<Settings>();
3375 let tab_size = settings.tab_size(language_name.as_deref()).get();
3376 let indent_kind = if settings.hard_tabs(language_name.as_deref()) {
3377 IndentKind::Tab
3378 } else {
3379 IndentKind::Space
3380 };
3381 let mut start_row = selection.start.row;
3382 let mut end_row = selection.end.row + 1;
3383
3384 // If a selection ends at the beginning of a line, don't indent
3385 // that last line.
3386 if selection.end.column == 0 {
3387 end_row -= 1;
3388 }
3389
3390 // Avoid re-indenting a row that has already been indented by a
3391 // previous selection, but still update this selection's column
3392 // to reflect that indentation.
3393 if delta_for_start_row > 0 {
3394 start_row += 1;
3395 selection.start.column += delta_for_start_row;
3396 if selection.end.row == selection.start.row {
3397 selection.end.column += delta_for_start_row;
3398 }
3399 }
3400
3401 let mut delta_for_end_row = 0;
3402 for row in start_row..end_row {
3403 let current_indent = snapshot.indent_size_for_line(row);
3404 let indent_delta = match (current_indent.kind, indent_kind) {
3405 (IndentKind::Space, IndentKind::Space) => {
3406 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
3407 IndentSize::spaces(columns_to_next_tab_stop)
3408 }
3409 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
3410 (_, IndentKind::Tab) => IndentSize::tab(),
3411 };
3412
3413 let row_start = Point::new(row, 0);
3414 edits.push((
3415 row_start..row_start,
3416 indent_delta.chars().collect::<String>(),
3417 ));
3418
3419 // Update this selection's endpoints to reflect the indentation.
3420 if row == selection.start.row {
3421 selection.start.column += indent_delta.len;
3422 }
3423 if row == selection.end.row {
3424 selection.end.column += indent_delta.len;
3425 delta_for_end_row = indent_delta.len;
3426 }
3427 }
3428
3429 if selection.start.row == selection.end.row {
3430 delta_for_start_row + delta_for_end_row
3431 } else {
3432 delta_for_end_row
3433 }
3434 }
3435
3436 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
3437 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3438 let selections = self.selections.all::<Point>(cx);
3439 let mut deletion_ranges = Vec::new();
3440 let mut last_outdent = None;
3441 {
3442 let buffer = self.buffer.read(cx);
3443 let snapshot = buffer.snapshot(cx);
3444 for selection in &selections {
3445 let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
3446 let tab_size = cx
3447 .global::<Settings>()
3448 .tab_size(language_name.as_deref())
3449 .get();
3450 let mut rows = selection.spanned_rows(false, &display_map);
3451
3452 // Avoid re-outdenting a row that has already been outdented by a
3453 // previous selection.
3454 if let Some(last_row) = last_outdent {
3455 if last_row == rows.start {
3456 rows.start += 1;
3457 }
3458 }
3459
3460 for row in rows {
3461 let indent_size = snapshot.indent_size_for_line(row);
3462 if indent_size.len > 0 {
3463 let deletion_len = match indent_size.kind {
3464 IndentKind::Space => {
3465 let columns_to_prev_tab_stop = indent_size.len % tab_size;
3466 if columns_to_prev_tab_stop == 0 {
3467 tab_size
3468 } else {
3469 columns_to_prev_tab_stop
3470 }
3471 }
3472 IndentKind::Tab => 1,
3473 };
3474 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
3475 last_outdent = Some(row);
3476 }
3477 }
3478 }
3479 }
3480
3481 self.transact(cx, |this, cx| {
3482 this.buffer.update(cx, |buffer, cx| {
3483 let empty_str: Arc<str> = "".into();
3484 buffer.edit(
3485 deletion_ranges
3486 .into_iter()
3487 .map(|range| (range, empty_str.clone())),
3488 None,
3489 cx,
3490 );
3491 });
3492 let selections = this.selections.all::<usize>(cx);
3493 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3494 });
3495 }
3496
3497 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
3498 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3499 let selections = self.selections.all::<Point>(cx);
3500
3501 let mut new_cursors = Vec::new();
3502 let mut edit_ranges = Vec::new();
3503 let mut selections = selections.iter().peekable();
3504 while let Some(selection) = selections.next() {
3505 let mut rows = selection.spanned_rows(false, &display_map);
3506 let goal_display_column = selection.head().to_display_point(&display_map).column();
3507
3508 // Accumulate contiguous regions of rows that we want to delete.
3509 while let Some(next_selection) = selections.peek() {
3510 let next_rows = next_selection.spanned_rows(false, &display_map);
3511 if next_rows.start <= rows.end {
3512 rows.end = next_rows.end;
3513 selections.next().unwrap();
3514 } else {
3515 break;
3516 }
3517 }
3518
3519 let buffer = &display_map.buffer_snapshot;
3520 let mut edit_start = Point::new(rows.start, 0).to_offset(buffer);
3521 let edit_end;
3522 let cursor_buffer_row;
3523 if buffer.max_point().row >= rows.end {
3524 // If there's a line after the range, delete the \n from the end of the row range
3525 // and position the cursor on the next line.
3526 edit_end = Point::new(rows.end, 0).to_offset(buffer);
3527 cursor_buffer_row = rows.end;
3528 } else {
3529 // If there isn't a line after the range, delete the \n from the line before the
3530 // start of the row range and position the cursor there.
3531 edit_start = edit_start.saturating_sub(1);
3532 edit_end = buffer.len();
3533 cursor_buffer_row = rows.start.saturating_sub(1);
3534 }
3535
3536 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
3537 *cursor.column_mut() =
3538 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
3539
3540 new_cursors.push((
3541 selection.id,
3542 buffer.anchor_after(cursor.to_point(&display_map)),
3543 ));
3544 edit_ranges.push(edit_start..edit_end);
3545 }
3546
3547 self.transact(cx, |this, cx| {
3548 let buffer = this.buffer.update(cx, |buffer, cx| {
3549 let empty_str: Arc<str> = "".into();
3550 buffer.edit(
3551 edit_ranges
3552 .into_iter()
3553 .map(|range| (range, empty_str.clone())),
3554 None,
3555 cx,
3556 );
3557 buffer.snapshot(cx)
3558 });
3559 let new_selections = new_cursors
3560 .into_iter()
3561 .map(|(id, cursor)| {
3562 let cursor = cursor.to_point(&buffer);
3563 Selection {
3564 id,
3565 start: cursor,
3566 end: cursor,
3567 reversed: false,
3568 goal: SelectionGoal::None,
3569 }
3570 })
3571 .collect();
3572
3573 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3574 s.select(new_selections);
3575 });
3576 });
3577 }
3578
3579 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
3580 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3581 let buffer = &display_map.buffer_snapshot;
3582 let selections = self.selections.all::<Point>(cx);
3583
3584 let mut edits = Vec::new();
3585 let mut selections_iter = selections.iter().peekable();
3586 while let Some(selection) = selections_iter.next() {
3587 // Avoid duplicating the same lines twice.
3588 let mut rows = selection.spanned_rows(false, &display_map);
3589
3590 while let Some(next_selection) = selections_iter.peek() {
3591 let next_rows = next_selection.spanned_rows(false, &display_map);
3592 if next_rows.start < rows.end {
3593 rows.end = next_rows.end;
3594 selections_iter.next().unwrap();
3595 } else {
3596 break;
3597 }
3598 }
3599
3600 // Copy the text from the selected row region and splice it at the start of the region.
3601 let start = Point::new(rows.start, 0);
3602 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
3603 let text = buffer
3604 .text_for_range(start..end)
3605 .chain(Some("\n"))
3606 .collect::<String>();
3607 edits.push((start..start, text));
3608 }
3609
3610 self.transact(cx, |this, cx| {
3611 this.buffer.update(cx, |buffer, cx| {
3612 buffer.edit(edits, None, cx);
3613 });
3614
3615 this.request_autoscroll(Autoscroll::fit(), cx);
3616 });
3617 }
3618
3619 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
3620 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3621 let buffer = self.buffer.read(cx).snapshot(cx);
3622
3623 let mut edits = Vec::new();
3624 let mut unfold_ranges = Vec::new();
3625 let mut refold_ranges = Vec::new();
3626
3627 let selections = self.selections.all::<Point>(cx);
3628 let mut selections = selections.iter().peekable();
3629 let mut contiguous_row_selections = Vec::new();
3630 let mut new_selections = Vec::new();
3631
3632 while let Some(selection) = selections.next() {
3633 // Find all the selections that span a contiguous row range
3634 let (start_row, end_row) = consume_contiguous_rows(
3635 &mut contiguous_row_selections,
3636 selection,
3637 &display_map,
3638 &mut selections,
3639 );
3640
3641 // Move the text spanned by the row range to be before the line preceding the row range
3642 if start_row > 0 {
3643 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
3644 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
3645 let insertion_point = display_map
3646 .prev_line_boundary(Point::new(start_row - 1, 0))
3647 .0;
3648
3649 // Don't move lines across excerpts
3650 if buffer
3651 .excerpt_boundaries_in_range((
3652 Bound::Excluded(insertion_point),
3653 Bound::Included(range_to_move.end),
3654 ))
3655 .next()
3656 .is_none()
3657 {
3658 let text = buffer
3659 .text_for_range(range_to_move.clone())
3660 .flat_map(|s| s.chars())
3661 .skip(1)
3662 .chain(['\n'])
3663 .collect::<String>();
3664
3665 edits.push((
3666 buffer.anchor_after(range_to_move.start)
3667 ..buffer.anchor_before(range_to_move.end),
3668 String::new(),
3669 ));
3670 let insertion_anchor = buffer.anchor_after(insertion_point);
3671 edits.push((insertion_anchor..insertion_anchor, text));
3672
3673 let row_delta = range_to_move.start.row - insertion_point.row + 1;
3674
3675 // Move selections up
3676 new_selections.extend(contiguous_row_selections.drain(..).map(
3677 |mut selection| {
3678 selection.start.row -= row_delta;
3679 selection.end.row -= row_delta;
3680 selection
3681 },
3682 ));
3683
3684 // Move folds up
3685 unfold_ranges.push(range_to_move.clone());
3686 for fold in display_map.folds_in_range(
3687 buffer.anchor_before(range_to_move.start)
3688 ..buffer.anchor_after(range_to_move.end),
3689 ) {
3690 let mut start = fold.start.to_point(&buffer);
3691 let mut end = fold.end.to_point(&buffer);
3692 start.row -= row_delta;
3693 end.row -= row_delta;
3694 refold_ranges.push(start..end);
3695 }
3696 }
3697 }
3698
3699 // If we didn't move line(s), preserve the existing selections
3700 new_selections.append(&mut contiguous_row_selections);
3701 }
3702
3703 self.transact(cx, |this, cx| {
3704 this.unfold_ranges(unfold_ranges, true, true, cx);
3705 this.buffer.update(cx, |buffer, cx| {
3706 for (range, text) in edits {
3707 buffer.edit([(range, text)], None, cx);
3708 }
3709 });
3710 this.fold_ranges(refold_ranges, true, cx);
3711 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3712 s.select(new_selections);
3713 })
3714 });
3715 }
3716
3717 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
3718 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3719 let buffer = self.buffer.read(cx).snapshot(cx);
3720
3721 let mut edits = Vec::new();
3722 let mut unfold_ranges = Vec::new();
3723 let mut refold_ranges = Vec::new();
3724
3725 let selections = self.selections.all::<Point>(cx);
3726 let mut selections = selections.iter().peekable();
3727 let mut contiguous_row_selections = Vec::new();
3728 let mut new_selections = Vec::new();
3729
3730 while let Some(selection) = selections.next() {
3731 // Find all the selections that span a contiguous row range
3732 let (start_row, end_row) = consume_contiguous_rows(
3733 &mut contiguous_row_selections,
3734 selection,
3735 &display_map,
3736 &mut selections,
3737 );
3738
3739 // Move the text spanned by the row range to be after the last line of the row range
3740 if end_row <= buffer.max_point().row {
3741 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
3742 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
3743
3744 // Don't move lines across excerpt boundaries
3745 if buffer
3746 .excerpt_boundaries_in_range((
3747 Bound::Excluded(range_to_move.start),
3748 Bound::Included(insertion_point),
3749 ))
3750 .next()
3751 .is_none()
3752 {
3753 let mut text = String::from("\n");
3754 text.extend(buffer.text_for_range(range_to_move.clone()));
3755 text.pop(); // Drop trailing newline
3756 edits.push((
3757 buffer.anchor_after(range_to_move.start)
3758 ..buffer.anchor_before(range_to_move.end),
3759 String::new(),
3760 ));
3761 let insertion_anchor = buffer.anchor_after(insertion_point);
3762 edits.push((insertion_anchor..insertion_anchor, text));
3763
3764 let row_delta = insertion_point.row - range_to_move.end.row + 1;
3765
3766 // Move selections down
3767 new_selections.extend(contiguous_row_selections.drain(..).map(
3768 |mut selection| {
3769 selection.start.row += row_delta;
3770 selection.end.row += row_delta;
3771 selection
3772 },
3773 ));
3774
3775 // Move folds down
3776 unfold_ranges.push(range_to_move.clone());
3777 for fold in display_map.folds_in_range(
3778 buffer.anchor_before(range_to_move.start)
3779 ..buffer.anchor_after(range_to_move.end),
3780 ) {
3781 let mut start = fold.start.to_point(&buffer);
3782 let mut end = fold.end.to_point(&buffer);
3783 start.row += row_delta;
3784 end.row += row_delta;
3785 refold_ranges.push(start..end);
3786 }
3787 }
3788 }
3789
3790 // If we didn't move line(s), preserve the existing selections
3791 new_selections.append(&mut contiguous_row_selections);
3792 }
3793
3794 self.transact(cx, |this, cx| {
3795 this.unfold_ranges(unfold_ranges, true, true, cx);
3796 this.buffer.update(cx, |buffer, cx| {
3797 for (range, text) in edits {
3798 buffer.edit([(range, text)], None, cx);
3799 }
3800 });
3801 this.fold_ranges(refold_ranges, true, cx);
3802 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
3803 });
3804 }
3805
3806 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
3807 self.transact(cx, |this, cx| {
3808 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3809 let mut edits: Vec<(Range<usize>, String)> = Default::default();
3810 let line_mode = s.line_mode;
3811 s.move_with(|display_map, selection| {
3812 if !selection.is_empty() || line_mode {
3813 return;
3814 }
3815
3816 let mut head = selection.head();
3817 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
3818 if head.column() == display_map.line_len(head.row()) {
3819 transpose_offset = display_map
3820 .buffer_snapshot
3821 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
3822 }
3823
3824 if transpose_offset == 0 {
3825 return;
3826 }
3827
3828 *head.column_mut() += 1;
3829 head = display_map.clip_point(head, Bias::Right);
3830 selection.collapse_to(head, SelectionGoal::Column(head.column()));
3831
3832 let transpose_start = display_map
3833 .buffer_snapshot
3834 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
3835 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
3836 let transpose_end = display_map
3837 .buffer_snapshot
3838 .clip_offset(transpose_offset + 1, Bias::Right);
3839 if let Some(ch) =
3840 display_map.buffer_snapshot.chars_at(transpose_start).next()
3841 {
3842 edits.push((transpose_start..transpose_offset, String::new()));
3843 edits.push((transpose_end..transpose_end, ch.to_string()));
3844 }
3845 }
3846 });
3847 edits
3848 });
3849 this.buffer
3850 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
3851 let selections = this.selections.all::<usize>(cx);
3852 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3853 s.select(selections);
3854 });
3855 });
3856 }
3857
3858 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
3859 let mut text = String::new();
3860 let buffer = self.buffer.read(cx).snapshot(cx);
3861 let mut selections = self.selections.all::<Point>(cx);
3862 let mut clipboard_selections = Vec::with_capacity(selections.len());
3863 {
3864 let max_point = buffer.max_point();
3865 for selection in &mut selections {
3866 let is_entire_line = selection.is_empty() || self.selections.line_mode;
3867 if is_entire_line {
3868 selection.start = Point::new(selection.start.row, 0);
3869 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
3870 selection.goal = SelectionGoal::None;
3871 }
3872 let mut len = 0;
3873 for chunk in buffer.text_for_range(selection.start..selection.end) {
3874 text.push_str(chunk);
3875 len += chunk.len();
3876 }
3877 clipboard_selections.push(ClipboardSelection {
3878 len,
3879 is_entire_line,
3880 first_line_indent: buffer.indent_size_for_line(selection.start.row).len,
3881 });
3882 }
3883 }
3884
3885 self.transact(cx, |this, cx| {
3886 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3887 s.select(selections);
3888 });
3889 this.insert("", cx);
3890 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
3891 });
3892 }
3893
3894 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
3895 let selections = self.selections.all::<Point>(cx);
3896 let buffer = self.buffer.read(cx).read(cx);
3897 let mut text = String::new();
3898
3899 let mut clipboard_selections = Vec::with_capacity(selections.len());
3900 {
3901 let max_point = buffer.max_point();
3902 for selection in selections.iter() {
3903 let mut start = selection.start;
3904 let mut end = selection.end;
3905 let is_entire_line = selection.is_empty() || self.selections.line_mode;
3906 if is_entire_line {
3907 start = Point::new(start.row, 0);
3908 end = cmp::min(max_point, Point::new(end.row + 1, 0));
3909 }
3910 let mut len = 0;
3911 for chunk in buffer.text_for_range(start..end) {
3912 text.push_str(chunk);
3913 len += chunk.len();
3914 }
3915 clipboard_selections.push(ClipboardSelection {
3916 len,
3917 is_entire_line,
3918 first_line_indent: buffer.indent_size_for_line(start.row).len,
3919 });
3920 }
3921 }
3922
3923 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
3924 }
3925
3926 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
3927 self.transact(cx, |this, cx| {
3928 if let Some(item) = cx.as_mut().read_from_clipboard() {
3929 let mut clipboard_text = Cow::Borrowed(item.text());
3930 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
3931 let old_selections = this.selections.all::<usize>(cx);
3932 let all_selections_were_entire_line =
3933 clipboard_selections.iter().all(|s| s.is_entire_line);
3934 let first_selection_indent_column =
3935 clipboard_selections.first().map(|s| s.first_line_indent);
3936 if clipboard_selections.len() != old_selections.len() {
3937 let mut newline_separated_text = String::new();
3938 let mut clipboard_selections = clipboard_selections.drain(..).peekable();
3939 let mut ix = 0;
3940 while let Some(clipboard_selection) = clipboard_selections.next() {
3941 newline_separated_text
3942 .push_str(&clipboard_text[ix..ix + clipboard_selection.len]);
3943 ix += clipboard_selection.len;
3944 if clipboard_selections.peek().is_some() {
3945 newline_separated_text.push('\n');
3946 }
3947 }
3948 clipboard_text = Cow::Owned(newline_separated_text);
3949 }
3950
3951 this.buffer.update(cx, |buffer, cx| {
3952 let snapshot = buffer.read(cx);
3953 let mut start_offset = 0;
3954 let mut edits = Vec::new();
3955 let mut original_indent_columns = Vec::new();
3956 let line_mode = this.selections.line_mode;
3957 for (ix, selection) in old_selections.iter().enumerate() {
3958 let to_insert;
3959 let entire_line;
3960 let original_indent_column;
3961 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
3962 let end_offset = start_offset + clipboard_selection.len;
3963 to_insert = &clipboard_text[start_offset..end_offset];
3964 entire_line = clipboard_selection.is_entire_line;
3965 start_offset = end_offset;
3966 original_indent_column =
3967 Some(clipboard_selection.first_line_indent);
3968 } else {
3969 to_insert = clipboard_text.as_str();
3970 entire_line = all_selections_were_entire_line;
3971 original_indent_column = first_selection_indent_column
3972 }
3973
3974 // If the corresponding selection was empty when this slice of the
3975 // clipboard text was written, then the entire line containing the
3976 // selection was copied. If this selection is also currently empty,
3977 // then paste the line before the current line of the buffer.
3978 let range = if selection.is_empty() && !line_mode && entire_line {
3979 let column = selection.start.to_point(&snapshot).column as usize;
3980 let line_start = selection.start - column;
3981 line_start..line_start
3982 } else {
3983 selection.range()
3984 };
3985
3986 edits.push((range, to_insert));
3987 original_indent_columns.extend(original_indent_column);
3988 }
3989 drop(snapshot);
3990
3991 buffer.edit(
3992 edits,
3993 Some(AutoindentMode::Block {
3994 original_indent_columns,
3995 }),
3996 cx,
3997 );
3998 });
3999
4000 let selections = this.selections.all::<usize>(cx);
4001 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4002 } else {
4003 this.insert(&clipboard_text, cx);
4004 }
4005 }
4006 });
4007 }
4008
4009 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
4010 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
4011 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
4012 self.change_selections(None, cx, |s| {
4013 s.select_anchors(selections.to_vec());
4014 });
4015 }
4016 self.request_autoscroll(Autoscroll::fit(), cx);
4017 self.unmark_text(cx);
4018 self.refresh_copilot_suggestions(cx);
4019 cx.emit(Event::Edited);
4020 }
4021 }
4022
4023 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
4024 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
4025 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
4026 {
4027 self.change_selections(None, cx, |s| {
4028 s.select_anchors(selections.to_vec());
4029 });
4030 }
4031 self.request_autoscroll(Autoscroll::fit(), cx);
4032 self.unmark_text(cx);
4033 self.refresh_copilot_suggestions(cx);
4034 cx.emit(Event::Edited);
4035 }
4036 }
4037
4038 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
4039 self.buffer
4040 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
4041 }
4042
4043 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
4044 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4045 let line_mode = s.line_mode;
4046 s.move_with(|map, selection| {
4047 let cursor = if selection.is_empty() && !line_mode {
4048 movement::left(map, selection.start)
4049 } else {
4050 selection.start
4051 };
4052 selection.collapse_to(cursor, SelectionGoal::None);
4053 });
4054 })
4055 }
4056
4057 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
4058 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4059 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
4060 })
4061 }
4062
4063 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
4064 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4065 let line_mode = s.line_mode;
4066 s.move_with(|map, selection| {
4067 let cursor = if selection.is_empty() && !line_mode {
4068 movement::right(map, selection.end)
4069 } else {
4070 selection.end
4071 };
4072 selection.collapse_to(cursor, SelectionGoal::None)
4073 });
4074 })
4075 }
4076
4077 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
4078 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4079 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
4080 })
4081 }
4082
4083 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
4084 if self.take_rename(true, cx).is_some() {
4085 return;
4086 }
4087
4088 if let Some(context_menu) = self.context_menu.as_mut() {
4089 if context_menu.select_prev(cx) {
4090 return;
4091 }
4092 }
4093
4094 if matches!(self.mode, EditorMode::SingleLine) {
4095 cx.propagate_action();
4096 return;
4097 }
4098
4099 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4100 let line_mode = s.line_mode;
4101 s.move_with(|map, selection| {
4102 if !selection.is_empty() && !line_mode {
4103 selection.goal = SelectionGoal::None;
4104 }
4105 let (cursor, goal) = movement::up(map, selection.start, selection.goal, false);
4106 selection.collapse_to(cursor, goal);
4107 });
4108 })
4109 }
4110
4111 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
4112 if self.take_rename(true, cx).is_some() {
4113 return;
4114 }
4115
4116 if self
4117 .context_menu
4118 .as_mut()
4119 .map(|menu| menu.select_first(cx))
4120 .unwrap_or(false)
4121 {
4122 return;
4123 }
4124
4125 if matches!(self.mode, EditorMode::SingleLine) {
4126 cx.propagate_action();
4127 return;
4128 }
4129
4130 let row_count = if let Some(row_count) = self.visible_line_count() {
4131 row_count as u32 - 1
4132 } else {
4133 return;
4134 };
4135
4136 let autoscroll = if action.center_cursor {
4137 Autoscroll::center()
4138 } else {
4139 Autoscroll::fit()
4140 };
4141
4142 self.change_selections(Some(autoscroll), cx, |s| {
4143 let line_mode = s.line_mode;
4144 s.move_with(|map, selection| {
4145 if !selection.is_empty() && !line_mode {
4146 selection.goal = SelectionGoal::None;
4147 }
4148 let (cursor, goal) =
4149 movement::up_by_rows(map, selection.end, row_count, selection.goal, false);
4150 selection.collapse_to(cursor, goal);
4151 });
4152 });
4153 }
4154
4155 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
4156 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4157 s.move_heads_with(|map, head, goal| movement::up(map, head, goal, false))
4158 })
4159 }
4160
4161 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
4162 self.take_rename(true, cx);
4163
4164 if let Some(context_menu) = self.context_menu.as_mut() {
4165 if context_menu.select_next(cx) {
4166 return;
4167 }
4168 }
4169
4170 if self.mode == EditorMode::SingleLine {
4171 cx.propagate_action();
4172 return;
4173 }
4174
4175 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4176 let line_mode = s.line_mode;
4177 s.move_with(|map, selection| {
4178 if !selection.is_empty() && !line_mode {
4179 selection.goal = SelectionGoal::None;
4180 }
4181 let (cursor, goal) = movement::down(map, selection.end, selection.goal, false);
4182 selection.collapse_to(cursor, goal);
4183 });
4184 });
4185 }
4186
4187 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
4188 if self.take_rename(true, cx).is_some() {
4189 return;
4190 }
4191
4192 if self
4193 .context_menu
4194 .as_mut()
4195 .map(|menu| menu.select_last(cx))
4196 .unwrap_or(false)
4197 {
4198 return;
4199 }
4200
4201 if matches!(self.mode, EditorMode::SingleLine) {
4202 cx.propagate_action();
4203 return;
4204 }
4205
4206 let row_count = if let Some(row_count) = self.visible_line_count() {
4207 row_count as u32 - 1
4208 } else {
4209 return;
4210 };
4211
4212 let autoscroll = if action.center_cursor {
4213 Autoscroll::center()
4214 } else {
4215 Autoscroll::fit()
4216 };
4217
4218 self.change_selections(Some(autoscroll), cx, |s| {
4219 let line_mode = s.line_mode;
4220 s.move_with(|map, selection| {
4221 if !selection.is_empty() && !line_mode {
4222 selection.goal = SelectionGoal::None;
4223 }
4224 let (cursor, goal) =
4225 movement::down_by_rows(map, selection.end, row_count, selection.goal, false);
4226 selection.collapse_to(cursor, goal);
4227 });
4228 });
4229 }
4230
4231 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
4232 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4233 s.move_heads_with(|map, head, goal| movement::down(map, head, goal, false))
4234 });
4235 }
4236
4237 pub fn move_to_previous_word_start(
4238 &mut self,
4239 _: &MoveToPreviousWordStart,
4240 cx: &mut ViewContext<Self>,
4241 ) {
4242 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4243 s.move_cursors_with(|map, head, _| {
4244 (
4245 movement::previous_word_start(map, head),
4246 SelectionGoal::None,
4247 )
4248 });
4249 })
4250 }
4251
4252 pub fn move_to_previous_subword_start(
4253 &mut self,
4254 _: &MoveToPreviousSubwordStart,
4255 cx: &mut ViewContext<Self>,
4256 ) {
4257 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4258 s.move_cursors_with(|map, head, _| {
4259 (
4260 movement::previous_subword_start(map, head),
4261 SelectionGoal::None,
4262 )
4263 });
4264 })
4265 }
4266
4267 pub fn select_to_previous_word_start(
4268 &mut self,
4269 _: &SelectToPreviousWordStart,
4270 cx: &mut ViewContext<Self>,
4271 ) {
4272 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4273 s.move_heads_with(|map, head, _| {
4274 (
4275 movement::previous_word_start(map, head),
4276 SelectionGoal::None,
4277 )
4278 });
4279 })
4280 }
4281
4282 pub fn select_to_previous_subword_start(
4283 &mut self,
4284 _: &SelectToPreviousSubwordStart,
4285 cx: &mut ViewContext<Self>,
4286 ) {
4287 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4288 s.move_heads_with(|map, head, _| {
4289 (
4290 movement::previous_subword_start(map, head),
4291 SelectionGoal::None,
4292 )
4293 });
4294 })
4295 }
4296
4297 pub fn delete_to_previous_word_start(
4298 &mut self,
4299 _: &DeleteToPreviousWordStart,
4300 cx: &mut ViewContext<Self>,
4301 ) {
4302 self.transact(cx, |this, cx| {
4303 this.select_autoclose_pair(cx);
4304 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4305 let line_mode = s.line_mode;
4306 s.move_with(|map, selection| {
4307 if selection.is_empty() && !line_mode {
4308 let cursor = movement::previous_word_start(map, selection.head());
4309 selection.set_head(cursor, SelectionGoal::None);
4310 }
4311 });
4312 });
4313 this.insert("", cx);
4314 });
4315 }
4316
4317 pub fn delete_to_previous_subword_start(
4318 &mut self,
4319 _: &DeleteToPreviousSubwordStart,
4320 cx: &mut ViewContext<Self>,
4321 ) {
4322 self.transact(cx, |this, cx| {
4323 this.select_autoclose_pair(cx);
4324 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4325 let line_mode = s.line_mode;
4326 s.move_with(|map, selection| {
4327 if selection.is_empty() && !line_mode {
4328 let cursor = movement::previous_subword_start(map, selection.head());
4329 selection.set_head(cursor, SelectionGoal::None);
4330 }
4331 });
4332 });
4333 this.insert("", cx);
4334 });
4335 }
4336
4337 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
4338 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4339 s.move_cursors_with(|map, head, _| {
4340 (movement::next_word_end(map, head), SelectionGoal::None)
4341 });
4342 })
4343 }
4344
4345 pub fn move_to_next_subword_end(
4346 &mut self,
4347 _: &MoveToNextSubwordEnd,
4348 cx: &mut ViewContext<Self>,
4349 ) {
4350 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4351 s.move_cursors_with(|map, head, _| {
4352 (movement::next_subword_end(map, head), SelectionGoal::None)
4353 });
4354 })
4355 }
4356
4357 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
4358 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4359 s.move_heads_with(|map, head, _| {
4360 (movement::next_word_end(map, head), SelectionGoal::None)
4361 });
4362 })
4363 }
4364
4365 pub fn select_to_next_subword_end(
4366 &mut self,
4367 _: &SelectToNextSubwordEnd,
4368 cx: &mut ViewContext<Self>,
4369 ) {
4370 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4371 s.move_heads_with(|map, head, _| {
4372 (movement::next_subword_end(map, head), SelectionGoal::None)
4373 });
4374 })
4375 }
4376
4377 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
4378 self.transact(cx, |this, cx| {
4379 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4380 let line_mode = s.line_mode;
4381 s.move_with(|map, selection| {
4382 if selection.is_empty() && !line_mode {
4383 let cursor = movement::next_word_end(map, selection.head());
4384 selection.set_head(cursor, SelectionGoal::None);
4385 }
4386 });
4387 });
4388 this.insert("", cx);
4389 });
4390 }
4391
4392 pub fn delete_to_next_subword_end(
4393 &mut self,
4394 _: &DeleteToNextSubwordEnd,
4395 cx: &mut ViewContext<Self>,
4396 ) {
4397 self.transact(cx, |this, cx| {
4398 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4399 s.move_with(|map, selection| {
4400 if selection.is_empty() {
4401 let cursor = movement::next_subword_end(map, selection.head());
4402 selection.set_head(cursor, SelectionGoal::None);
4403 }
4404 });
4405 });
4406 this.insert("", cx);
4407 });
4408 }
4409
4410 pub fn move_to_beginning_of_line(
4411 &mut self,
4412 _: &MoveToBeginningOfLine,
4413 cx: &mut ViewContext<Self>,
4414 ) {
4415 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4416 s.move_cursors_with(|map, head, _| {
4417 (
4418 movement::indented_line_beginning(map, head, true),
4419 SelectionGoal::None,
4420 )
4421 });
4422 })
4423 }
4424
4425 pub fn select_to_beginning_of_line(
4426 &mut self,
4427 action: &SelectToBeginningOfLine,
4428 cx: &mut ViewContext<Self>,
4429 ) {
4430 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4431 s.move_heads_with(|map, head, _| {
4432 (
4433 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
4434 SelectionGoal::None,
4435 )
4436 });
4437 });
4438 }
4439
4440 pub fn delete_to_beginning_of_line(
4441 &mut self,
4442 _: &DeleteToBeginningOfLine,
4443 cx: &mut ViewContext<Self>,
4444 ) {
4445 self.transact(cx, |this, cx| {
4446 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4447 s.move_with(|_, selection| {
4448 selection.reversed = true;
4449 });
4450 });
4451
4452 this.select_to_beginning_of_line(
4453 &SelectToBeginningOfLine {
4454 stop_at_soft_wraps: false,
4455 },
4456 cx,
4457 );
4458 this.backspace(&Backspace, cx);
4459 });
4460 }
4461
4462 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
4463 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4464 s.move_cursors_with(|map, head, _| {
4465 (movement::line_end(map, head, true), SelectionGoal::None)
4466 });
4467 })
4468 }
4469
4470 pub fn select_to_end_of_line(
4471 &mut self,
4472 action: &SelectToEndOfLine,
4473 cx: &mut ViewContext<Self>,
4474 ) {
4475 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4476 s.move_heads_with(|map, head, _| {
4477 (
4478 movement::line_end(map, head, action.stop_at_soft_wraps),
4479 SelectionGoal::None,
4480 )
4481 });
4482 })
4483 }
4484
4485 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
4486 self.transact(cx, |this, cx| {
4487 this.select_to_end_of_line(
4488 &SelectToEndOfLine {
4489 stop_at_soft_wraps: false,
4490 },
4491 cx,
4492 );
4493 this.delete(&Delete, cx);
4494 });
4495 }
4496
4497 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
4498 self.transact(cx, |this, cx| {
4499 this.select_to_end_of_line(
4500 &SelectToEndOfLine {
4501 stop_at_soft_wraps: false,
4502 },
4503 cx,
4504 );
4505 this.cut(&Cut, cx);
4506 });
4507 }
4508
4509 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
4510 if matches!(self.mode, EditorMode::SingleLine) {
4511 cx.propagate_action();
4512 return;
4513 }
4514
4515 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4516 s.select_ranges(vec![0..0]);
4517 });
4518 }
4519
4520 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
4521 let mut selection = self.selections.last::<Point>(cx);
4522 selection.set_head(Point::zero(), SelectionGoal::None);
4523
4524 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4525 s.select(vec![selection]);
4526 });
4527 }
4528
4529 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
4530 if matches!(self.mode, EditorMode::SingleLine) {
4531 cx.propagate_action();
4532 return;
4533 }
4534
4535 let cursor = self.buffer.read(cx).read(cx).len();
4536 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4537 s.select_ranges(vec![cursor..cursor])
4538 });
4539 }
4540
4541 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
4542 self.nav_history = nav_history;
4543 }
4544
4545 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
4546 self.nav_history.as_ref()
4547 }
4548
4549 fn push_to_nav_history(
4550 &self,
4551 cursor_anchor: Anchor,
4552 new_position: Option<Point>,
4553 cx: &mut ViewContext<Self>,
4554 ) {
4555 if let Some(nav_history) = &self.nav_history {
4556 let buffer = self.buffer.read(cx).read(cx);
4557 let cursor_position = cursor_anchor.to_point(&buffer);
4558 let scroll_state = self.scroll_manager.anchor();
4559 let scroll_top_row = scroll_state.top_row(&buffer);
4560 drop(buffer);
4561
4562 if let Some(new_position) = new_position {
4563 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
4564 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
4565 return;
4566 }
4567 }
4568
4569 nav_history.push(
4570 Some(NavigationData {
4571 cursor_anchor,
4572 cursor_position,
4573 scroll_anchor: scroll_state,
4574 scroll_top_row,
4575 }),
4576 cx,
4577 );
4578 }
4579 }
4580
4581 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
4582 let buffer = self.buffer.read(cx).snapshot(cx);
4583 let mut selection = self.selections.first::<usize>(cx);
4584 selection.set_head(buffer.len(), SelectionGoal::None);
4585 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4586 s.select(vec![selection]);
4587 });
4588 }
4589
4590 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
4591 let end = self.buffer.read(cx).read(cx).len();
4592 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4593 s.select_ranges(vec![0..end]);
4594 });
4595 }
4596
4597 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
4598 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4599 let mut selections = self.selections.all::<Point>(cx);
4600 let max_point = display_map.buffer_snapshot.max_point();
4601 for selection in &mut selections {
4602 let rows = selection.spanned_rows(true, &display_map);
4603 selection.start = Point::new(rows.start, 0);
4604 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
4605 selection.reversed = false;
4606 }
4607 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4608 s.select(selections);
4609 });
4610 }
4611
4612 pub fn split_selection_into_lines(
4613 &mut self,
4614 _: &SplitSelectionIntoLines,
4615 cx: &mut ViewContext<Self>,
4616 ) {
4617 let mut to_unfold = Vec::new();
4618 let mut new_selection_ranges = Vec::new();
4619 {
4620 let selections = self.selections.all::<Point>(cx);
4621 let buffer = self.buffer.read(cx).read(cx);
4622 for selection in selections {
4623 for row in selection.start.row..selection.end.row {
4624 let cursor = Point::new(row, buffer.line_len(row));
4625 new_selection_ranges.push(cursor..cursor);
4626 }
4627 new_selection_ranges.push(selection.end..selection.end);
4628 to_unfold.push(selection.start..selection.end);
4629 }
4630 }
4631 self.unfold_ranges(to_unfold, true, true, cx);
4632 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4633 s.select_ranges(new_selection_ranges);
4634 });
4635 }
4636
4637 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
4638 self.add_selection(true, cx);
4639 }
4640
4641 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
4642 self.add_selection(false, cx);
4643 }
4644
4645 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
4646 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4647 let mut selections = self.selections.all::<Point>(cx);
4648 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
4649 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
4650 let range = oldest_selection.display_range(&display_map).sorted();
4651 let columns = cmp::min(range.start.column(), range.end.column())
4652 ..cmp::max(range.start.column(), range.end.column());
4653
4654 selections.clear();
4655 let mut stack = Vec::new();
4656 for row in range.start.row()..=range.end.row() {
4657 if let Some(selection) = self.selections.build_columnar_selection(
4658 &display_map,
4659 row,
4660 &columns,
4661 oldest_selection.reversed,
4662 ) {
4663 stack.push(selection.id);
4664 selections.push(selection);
4665 }
4666 }
4667
4668 if above {
4669 stack.reverse();
4670 }
4671
4672 AddSelectionsState { above, stack }
4673 });
4674
4675 let last_added_selection = *state.stack.last().unwrap();
4676 let mut new_selections = Vec::new();
4677 if above == state.above {
4678 let end_row = if above {
4679 0
4680 } else {
4681 display_map.max_point().row()
4682 };
4683
4684 'outer: for selection in selections {
4685 if selection.id == last_added_selection {
4686 let range = selection.display_range(&display_map).sorted();
4687 debug_assert_eq!(range.start.row(), range.end.row());
4688 let mut row = range.start.row();
4689 let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
4690 {
4691 start..end
4692 } else {
4693 cmp::min(range.start.column(), range.end.column())
4694 ..cmp::max(range.start.column(), range.end.column())
4695 };
4696
4697 while row != end_row {
4698 if above {
4699 row -= 1;
4700 } else {
4701 row += 1;
4702 }
4703
4704 if let Some(new_selection) = self.selections.build_columnar_selection(
4705 &display_map,
4706 row,
4707 &columns,
4708 selection.reversed,
4709 ) {
4710 state.stack.push(new_selection.id);
4711 if above {
4712 new_selections.push(new_selection);
4713 new_selections.push(selection);
4714 } else {
4715 new_selections.push(selection);
4716 new_selections.push(new_selection);
4717 }
4718
4719 continue 'outer;
4720 }
4721 }
4722 }
4723
4724 new_selections.push(selection);
4725 }
4726 } else {
4727 new_selections = selections;
4728 new_selections.retain(|s| s.id != last_added_selection);
4729 state.stack.pop();
4730 }
4731
4732 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4733 s.select(new_selections);
4734 });
4735 if state.stack.len() > 1 {
4736 self.add_selections_state = Some(state);
4737 }
4738 }
4739
4740 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) {
4741 self.push_to_selection_history();
4742 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4743 let buffer = &display_map.buffer_snapshot;
4744 let mut selections = self.selections.all::<usize>(cx);
4745 if let Some(mut select_next_state) = self.select_next_state.take() {
4746 let query = &select_next_state.query;
4747 if !select_next_state.done {
4748 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
4749 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
4750 let mut next_selected_range = None;
4751
4752 let bytes_after_last_selection =
4753 buffer.bytes_in_range(last_selection.end..buffer.len());
4754 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
4755 let query_matches = query
4756 .stream_find_iter(bytes_after_last_selection)
4757 .map(|result| (last_selection.end, result))
4758 .chain(
4759 query
4760 .stream_find_iter(bytes_before_first_selection)
4761 .map(|result| (0, result)),
4762 );
4763 for (start_offset, query_match) in query_matches {
4764 let query_match = query_match.unwrap(); // can only fail due to I/O
4765 let offset_range =
4766 start_offset + query_match.start()..start_offset + query_match.end();
4767 let display_range = offset_range.start.to_display_point(&display_map)
4768 ..offset_range.end.to_display_point(&display_map);
4769
4770 if !select_next_state.wordwise
4771 || (!movement::is_inside_word(&display_map, display_range.start)
4772 && !movement::is_inside_word(&display_map, display_range.end))
4773 {
4774 next_selected_range = Some(offset_range);
4775 break;
4776 }
4777 }
4778
4779 if let Some(next_selected_range) = next_selected_range {
4780 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
4781 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
4782 if action.replace_newest {
4783 s.delete(s.newest_anchor().id);
4784 }
4785 s.insert_range(next_selected_range);
4786 });
4787 } else {
4788 select_next_state.done = true;
4789 }
4790 }
4791
4792 self.select_next_state = Some(select_next_state);
4793 } else if selections.len() == 1 {
4794 let selection = selections.last_mut().unwrap();
4795 if selection.start == selection.end {
4796 let word_range = movement::surrounding_word(
4797 &display_map,
4798 selection.start.to_display_point(&display_map),
4799 );
4800 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
4801 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
4802 selection.goal = SelectionGoal::None;
4803 selection.reversed = false;
4804
4805 let query = buffer
4806 .text_for_range(selection.start..selection.end)
4807 .collect::<String>();
4808 let select_state = SelectNextState {
4809 query: AhoCorasick::new_auto_configured(&[query]),
4810 wordwise: true,
4811 done: false,
4812 };
4813 self.unfold_ranges([selection.start..selection.end], false, true, cx);
4814 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
4815 s.select(selections);
4816 });
4817 self.select_next_state = Some(select_state);
4818 } else {
4819 let query = buffer
4820 .text_for_range(selection.start..selection.end)
4821 .collect::<String>();
4822 self.select_next_state = Some(SelectNextState {
4823 query: AhoCorasick::new_auto_configured(&[query]),
4824 wordwise: false,
4825 done: false,
4826 });
4827 self.select_next(action, cx);
4828 }
4829 }
4830 }
4831
4832 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
4833 self.transact(cx, |this, cx| {
4834 let mut selections = this.selections.all::<Point>(cx);
4835 let mut edits = Vec::new();
4836 let mut selection_edit_ranges = Vec::new();
4837 let mut last_toggled_row = None;
4838 let snapshot = this.buffer.read(cx).read(cx);
4839 let empty_str: Arc<str> = "".into();
4840 let mut suffixes_inserted = Vec::new();
4841
4842 fn comment_prefix_range(
4843 snapshot: &MultiBufferSnapshot,
4844 row: u32,
4845 comment_prefix: &str,
4846 comment_prefix_whitespace: &str,
4847 ) -> Range<Point> {
4848 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
4849
4850 let mut line_bytes = snapshot
4851 .bytes_in_range(start..snapshot.max_point())
4852 .flatten()
4853 .copied();
4854
4855 // If this line currently begins with the line comment prefix, then record
4856 // the range containing the prefix.
4857 if line_bytes
4858 .by_ref()
4859 .take(comment_prefix.len())
4860 .eq(comment_prefix.bytes())
4861 {
4862 // Include any whitespace that matches the comment prefix.
4863 let matching_whitespace_len = line_bytes
4864 .zip(comment_prefix_whitespace.bytes())
4865 .take_while(|(a, b)| a == b)
4866 .count() as u32;
4867 let end = Point::new(
4868 start.row,
4869 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
4870 );
4871 start..end
4872 } else {
4873 start..start
4874 }
4875 }
4876
4877 fn comment_suffix_range(
4878 snapshot: &MultiBufferSnapshot,
4879 row: u32,
4880 comment_suffix: &str,
4881 comment_suffix_has_leading_space: bool,
4882 ) -> Range<Point> {
4883 let end = Point::new(row, snapshot.line_len(row));
4884 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
4885
4886 let mut line_end_bytes = snapshot
4887 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
4888 .flatten()
4889 .copied();
4890
4891 let leading_space_len = if suffix_start_column > 0
4892 && line_end_bytes.next() == Some(b' ')
4893 && comment_suffix_has_leading_space
4894 {
4895 1
4896 } else {
4897 0
4898 };
4899
4900 // If this line currently begins with the line comment prefix, then record
4901 // the range containing the prefix.
4902 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
4903 let start = Point::new(end.row, suffix_start_column - leading_space_len);
4904 start..end
4905 } else {
4906 end..end
4907 }
4908 }
4909
4910 // TODO: Handle selections that cross excerpts
4911 for selection in &mut selections {
4912 let start_column = snapshot.indent_size_for_line(selection.start.row).len;
4913 let language = if let Some(language) =
4914 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
4915 {
4916 language
4917 } else {
4918 continue;
4919 };
4920
4921 selection_edit_ranges.clear();
4922
4923 // If multiple selections contain a given row, avoid processing that
4924 // row more than once.
4925 let mut start_row = selection.start.row;
4926 if last_toggled_row == Some(start_row) {
4927 start_row += 1;
4928 }
4929 let end_row =
4930 if selection.end.row > selection.start.row && selection.end.column == 0 {
4931 selection.end.row - 1
4932 } else {
4933 selection.end.row
4934 };
4935 last_toggled_row = Some(end_row);
4936
4937 if start_row > end_row {
4938 continue;
4939 }
4940
4941 // If the language has line comments, toggle those.
4942 if let Some(full_comment_prefix) = language.line_comment_prefix() {
4943 // Split the comment prefix's trailing whitespace into a separate string,
4944 // as that portion won't be used for detecting if a line is a comment.
4945 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
4946 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
4947 let mut all_selection_lines_are_comments = true;
4948
4949 for row in start_row..=end_row {
4950 if snapshot.is_line_blank(row) {
4951 continue;
4952 }
4953
4954 let prefix_range = comment_prefix_range(
4955 snapshot.deref(),
4956 row,
4957 comment_prefix,
4958 comment_prefix_whitespace,
4959 );
4960 if prefix_range.is_empty() {
4961 all_selection_lines_are_comments = false;
4962 }
4963 selection_edit_ranges.push(prefix_range);
4964 }
4965
4966 if all_selection_lines_are_comments {
4967 edits.extend(
4968 selection_edit_ranges
4969 .iter()
4970 .cloned()
4971 .map(|range| (range, empty_str.clone())),
4972 );
4973 } else {
4974 let min_column = selection_edit_ranges
4975 .iter()
4976 .map(|r| r.start.column)
4977 .min()
4978 .unwrap_or(0);
4979 edits.extend(selection_edit_ranges.iter().map(|range| {
4980 let position = Point::new(range.start.row, min_column);
4981 (position..position, full_comment_prefix.clone())
4982 }));
4983 }
4984 } else if let Some((full_comment_prefix, comment_suffix)) =
4985 language.block_comment_delimiters()
4986 {
4987 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
4988 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
4989 let prefix_range = comment_prefix_range(
4990 snapshot.deref(),
4991 start_row,
4992 comment_prefix,
4993 comment_prefix_whitespace,
4994 );
4995 let suffix_range = comment_suffix_range(
4996 snapshot.deref(),
4997 end_row,
4998 comment_suffix.trim_start_matches(' '),
4999 comment_suffix.starts_with(' '),
5000 );
5001
5002 if prefix_range.is_empty() || suffix_range.is_empty() {
5003 edits.push((
5004 prefix_range.start..prefix_range.start,
5005 full_comment_prefix.clone(),
5006 ));
5007 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
5008 suffixes_inserted.push((end_row, comment_suffix.len()));
5009 } else {
5010 edits.push((prefix_range, empty_str.clone()));
5011 edits.push((suffix_range, empty_str.clone()));
5012 }
5013 } else {
5014 continue;
5015 }
5016 }
5017
5018 drop(snapshot);
5019 this.buffer.update(cx, |buffer, cx| {
5020 buffer.edit(edits, None, cx);
5021 });
5022
5023 // Adjust selections so that they end before any comment suffixes that
5024 // were inserted.
5025 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
5026 let mut selections = this.selections.all::<Point>(cx);
5027 let snapshot = this.buffer.read(cx).read(cx);
5028 for selection in &mut selections {
5029 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
5030 match row.cmp(&selection.end.row) {
5031 Ordering::Less => {
5032 suffixes_inserted.next();
5033 continue;
5034 }
5035 Ordering::Greater => break,
5036 Ordering::Equal => {
5037 if selection.end.column == snapshot.line_len(row) {
5038 if selection.is_empty() {
5039 selection.start.column -= suffix_len as u32;
5040 }
5041 selection.end.column -= suffix_len as u32;
5042 }
5043 break;
5044 }
5045 }
5046 }
5047 }
5048
5049 drop(snapshot);
5050 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5051
5052 let selections = this.selections.all::<Point>(cx);
5053 let selections_on_single_row = selections.windows(2).all(|selections| {
5054 selections[0].start.row == selections[1].start.row
5055 && selections[0].end.row == selections[1].end.row
5056 && selections[0].start.row == selections[0].end.row
5057 });
5058 let selections_selecting = selections
5059 .iter()
5060 .any(|selection| selection.start != selection.end);
5061 let advance_downwards = action.advance_downwards
5062 && selections_on_single_row
5063 && !selections_selecting
5064 && this.mode != EditorMode::SingleLine;
5065
5066 if advance_downwards {
5067 let snapshot = this.buffer.read(cx).snapshot(cx);
5068
5069 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5070 s.move_cursors_with(|display_snapshot, display_point, _| {
5071 let mut point = display_point.to_point(display_snapshot);
5072 point.row += 1;
5073 point = snapshot.clip_point(point, Bias::Left);
5074 let display_point = point.to_display_point(display_snapshot);
5075 (display_point, SelectionGoal::Column(display_point.column()))
5076 })
5077 });
5078 }
5079 });
5080 }
5081
5082 pub fn select_larger_syntax_node(
5083 &mut self,
5084 _: &SelectLargerSyntaxNode,
5085 cx: &mut ViewContext<Self>,
5086 ) {
5087 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5088 let buffer = self.buffer.read(cx).snapshot(cx);
5089 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
5090
5091 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
5092 let mut selected_larger_node = false;
5093 let new_selections = old_selections
5094 .iter()
5095 .map(|selection| {
5096 let old_range = selection.start..selection.end;
5097 let mut new_range = old_range.clone();
5098 while let Some(containing_range) =
5099 buffer.range_for_syntax_ancestor(new_range.clone())
5100 {
5101 new_range = containing_range;
5102 if !display_map.intersects_fold(new_range.start)
5103 && !display_map.intersects_fold(new_range.end)
5104 {
5105 break;
5106 }
5107 }
5108
5109 selected_larger_node |= new_range != old_range;
5110 Selection {
5111 id: selection.id,
5112 start: new_range.start,
5113 end: new_range.end,
5114 goal: SelectionGoal::None,
5115 reversed: selection.reversed,
5116 }
5117 })
5118 .collect::<Vec<_>>();
5119
5120 if selected_larger_node {
5121 stack.push(old_selections);
5122 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5123 s.select(new_selections);
5124 });
5125 }
5126 self.select_larger_syntax_node_stack = stack;
5127 }
5128
5129 pub fn select_smaller_syntax_node(
5130 &mut self,
5131 _: &SelectSmallerSyntaxNode,
5132 cx: &mut ViewContext<Self>,
5133 ) {
5134 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
5135 if let Some(selections) = stack.pop() {
5136 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5137 s.select(selections.to_vec());
5138 });
5139 }
5140 self.select_larger_syntax_node_stack = stack;
5141 }
5142
5143 pub fn move_to_enclosing_bracket(
5144 &mut self,
5145 _: &MoveToEnclosingBracket,
5146 cx: &mut ViewContext<Self>,
5147 ) {
5148 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5149 s.move_offsets_with(|snapshot, selection| {
5150 let Some(enclosing_bracket_ranges) = snapshot.enclosing_bracket_ranges(selection.start..selection.end) else {
5151 return;
5152 };
5153
5154 let mut best_length = usize::MAX;
5155 let mut best_inside = false;
5156 let mut best_in_bracket_range = false;
5157 let mut best_destination = None;
5158 for (open, close) in enclosing_bracket_ranges {
5159 let close = close.to_inclusive();
5160 let length = close.end() - open.start;
5161 let inside = selection.start >= open.end && selection.end <= *close.start();
5162 let in_bracket_range = open.to_inclusive().contains(&selection.head()) || close.contains(&selection.head());
5163
5164 // If best is next to a bracket and current isn't, skip
5165 if !in_bracket_range && best_in_bracket_range {
5166 continue;
5167 }
5168
5169 // Prefer smaller lengths unless best is inside and current isn't
5170 if length > best_length && (best_inside || !inside) {
5171 continue;
5172 }
5173
5174 best_length = length;
5175 best_inside = inside;
5176 best_in_bracket_range = in_bracket_range;
5177 best_destination = Some(if close.contains(&selection.start) && close.contains(&selection.end) {
5178 if inside {
5179 open.end
5180 } else {
5181 open.start
5182 }
5183 } else {
5184 if inside {
5185 *close.start()
5186 } else {
5187 *close.end()
5188 }
5189 });
5190 }
5191
5192 if let Some(destination) = best_destination {
5193 selection.collapse_to(destination, SelectionGoal::None);
5194 }
5195 })
5196 });
5197 }
5198
5199 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
5200 self.end_selection(cx);
5201 self.selection_history.mode = SelectionHistoryMode::Undoing;
5202 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
5203 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
5204 self.select_next_state = entry.select_next_state;
5205 self.add_selections_state = entry.add_selections_state;
5206 self.request_autoscroll(Autoscroll::newest(), cx);
5207 }
5208 self.selection_history.mode = SelectionHistoryMode::Normal;
5209 }
5210
5211 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
5212 self.end_selection(cx);
5213 self.selection_history.mode = SelectionHistoryMode::Redoing;
5214 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
5215 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
5216 self.select_next_state = entry.select_next_state;
5217 self.add_selections_state = entry.add_selections_state;
5218 self.request_autoscroll(Autoscroll::newest(), cx);
5219 }
5220 self.selection_history.mode = SelectionHistoryMode::Normal;
5221 }
5222
5223 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
5224 self.go_to_diagnostic_impl(Direction::Next, cx)
5225 }
5226
5227 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
5228 self.go_to_diagnostic_impl(Direction::Prev, cx)
5229 }
5230
5231 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
5232 let buffer = self.buffer.read(cx).snapshot(cx);
5233 let selection = self.selections.newest::<usize>(cx);
5234
5235 // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
5236 if direction == Direction::Next {
5237 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
5238 let (group_id, jump_to) = popover.activation_info();
5239 if self.activate_diagnostics(group_id, cx) {
5240 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5241 let mut new_selection = s.newest_anchor().clone();
5242 new_selection.collapse_to(jump_to, SelectionGoal::None);
5243 s.select_anchors(vec![new_selection.clone()]);
5244 });
5245 }
5246 return;
5247 }
5248 }
5249
5250 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
5251 active_diagnostics
5252 .primary_range
5253 .to_offset(&buffer)
5254 .to_inclusive()
5255 });
5256 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
5257 if active_primary_range.contains(&selection.head()) {
5258 *active_primary_range.end()
5259 } else {
5260 selection.head()
5261 }
5262 } else {
5263 selection.head()
5264 };
5265
5266 loop {
5267 let mut diagnostics = if direction == Direction::Prev {
5268 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
5269 } else {
5270 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
5271 };
5272 let group = diagnostics.find_map(|entry| {
5273 if entry.diagnostic.is_primary
5274 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
5275 && !entry.range.is_empty()
5276 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
5277 {
5278 Some((entry.range, entry.diagnostic.group_id))
5279 } else {
5280 None
5281 }
5282 });
5283
5284 if let Some((primary_range, group_id)) = group {
5285 if self.activate_diagnostics(group_id, cx) {
5286 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5287 s.select(vec![Selection {
5288 id: selection.id,
5289 start: primary_range.start,
5290 end: primary_range.start,
5291 reversed: false,
5292 goal: SelectionGoal::None,
5293 }]);
5294 });
5295 }
5296 break;
5297 } else {
5298 // Cycle around to the start of the buffer, potentially moving back to the start of
5299 // the currently active diagnostic.
5300 active_primary_range.take();
5301 if direction == Direction::Prev {
5302 if search_start == buffer.len() {
5303 break;
5304 } else {
5305 search_start = buffer.len();
5306 }
5307 } else if search_start == 0 {
5308 break;
5309 } else {
5310 search_start = 0;
5311 }
5312 }
5313 }
5314 }
5315
5316 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
5317 self.go_to_hunk_impl(Direction::Next, cx)
5318 }
5319
5320 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
5321 self.go_to_hunk_impl(Direction::Prev, cx)
5322 }
5323
5324 pub fn go_to_hunk_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
5325 let snapshot = self
5326 .display_map
5327 .update(cx, |display_map, cx| display_map.snapshot(cx));
5328 let selection = self.selections.newest::<Point>(cx);
5329
5330 fn seek_in_direction(
5331 this: &mut Editor,
5332 snapshot: &DisplaySnapshot,
5333 initial_point: Point,
5334 is_wrapped: bool,
5335 direction: Direction,
5336 cx: &mut ViewContext<Editor>,
5337 ) -> bool {
5338 let hunks = if direction == Direction::Next {
5339 snapshot
5340 .buffer_snapshot
5341 .git_diff_hunks_in_range(initial_point.row..u32::MAX, false)
5342 } else {
5343 snapshot
5344 .buffer_snapshot
5345 .git_diff_hunks_in_range(0..initial_point.row, true)
5346 };
5347
5348 let display_point = initial_point.to_display_point(snapshot);
5349 let mut hunks = hunks
5350 .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
5351 .skip_while(|hunk| {
5352 if is_wrapped {
5353 false
5354 } else {
5355 hunk.contains_display_row(display_point.row())
5356 }
5357 })
5358 .dedup();
5359
5360 if let Some(hunk) = hunks.next() {
5361 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5362 let row = hunk.start_display_row();
5363 let point = DisplayPoint::new(row, 0);
5364 s.select_display_ranges([point..point]);
5365 });
5366
5367 true
5368 } else {
5369 false
5370 }
5371 }
5372
5373 if !seek_in_direction(self, &snapshot, selection.head(), false, direction, cx) {
5374 let wrapped_point = match direction {
5375 Direction::Next => Point::zero(),
5376 Direction::Prev => snapshot.buffer_snapshot.max_point(),
5377 };
5378 seek_in_direction(self, &snapshot, wrapped_point, true, direction, cx);
5379 }
5380 }
5381
5382 pub fn go_to_definition(
5383 workspace: &mut Workspace,
5384 _: &GoToDefinition,
5385 cx: &mut ViewContext<Workspace>,
5386 ) {
5387 Self::go_to_definition_of_kind(GotoDefinitionKind::Symbol, workspace, cx);
5388 }
5389
5390 pub fn go_to_type_definition(
5391 workspace: &mut Workspace,
5392 _: &GoToTypeDefinition,
5393 cx: &mut ViewContext<Workspace>,
5394 ) {
5395 Self::go_to_definition_of_kind(GotoDefinitionKind::Type, workspace, cx);
5396 }
5397
5398 fn go_to_definition_of_kind(
5399 kind: GotoDefinitionKind,
5400 workspace: &mut Workspace,
5401 cx: &mut ViewContext<Workspace>,
5402 ) {
5403 let active_item = workspace.active_item(cx);
5404 let editor_handle = if let Some(editor) = active_item
5405 .as_ref()
5406 .and_then(|item| item.act_as::<Self>(cx))
5407 {
5408 editor
5409 } else {
5410 return;
5411 };
5412
5413 let editor = editor_handle.read(cx);
5414 let buffer = editor.buffer.read(cx);
5415 let head = editor.selections.newest::<usize>(cx).head();
5416 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
5417 text_anchor
5418 } else {
5419 return;
5420 };
5421
5422 let project = workspace.project().clone();
5423 let definitions = project.update(cx, |project, cx| match kind {
5424 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
5425 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
5426 });
5427
5428 cx.spawn_labeled("Fetching Definition...", |workspace, mut cx| async move {
5429 let definitions = definitions.await?;
5430 workspace.update(&mut cx, |workspace, cx| {
5431 Editor::navigate_to_definitions(workspace, editor_handle, definitions, cx);
5432 });
5433
5434 Ok::<(), anyhow::Error>(())
5435 })
5436 .detach_and_log_err(cx);
5437 }
5438
5439 pub fn navigate_to_definitions(
5440 workspace: &mut Workspace,
5441 editor_handle: ViewHandle<Editor>,
5442 definitions: Vec<LocationLink>,
5443 cx: &mut ViewContext<Workspace>,
5444 ) {
5445 let pane = workspace.active_pane().clone();
5446 // If there is one definition, just open it directly
5447 if let [definition] = definitions.as_slice() {
5448 let range = definition
5449 .target
5450 .range
5451 .to_offset(definition.target.buffer.read(cx));
5452
5453 let target_editor_handle =
5454 workspace.open_project_item(definition.target.buffer.clone(), cx);
5455 target_editor_handle.update(cx, |target_editor, cx| {
5456 // When selecting a definition in a different buffer, disable the nav history
5457 // to avoid creating a history entry at the previous cursor location.
5458 if editor_handle != target_editor_handle {
5459 pane.update(cx, |pane, _| pane.disable_history());
5460 }
5461 target_editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
5462 s.select_ranges([range]);
5463 });
5464
5465 pane.update(cx, |pane, _| pane.enable_history());
5466 });
5467 } else if !definitions.is_empty() {
5468 let replica_id = editor_handle.read(cx).replica_id(cx);
5469 let title = definitions
5470 .iter()
5471 .find(|definition| definition.origin.is_some())
5472 .and_then(|definition| {
5473 definition.origin.as_ref().map(|origin| {
5474 let buffer = origin.buffer.read(cx);
5475 format!(
5476 "Definitions for {}",
5477 buffer
5478 .text_for_range(origin.range.clone())
5479 .collect::<String>()
5480 )
5481 })
5482 })
5483 .unwrap_or("Definitions".to_owned());
5484 let locations = definitions
5485 .into_iter()
5486 .map(|definition| definition.target)
5487 .collect();
5488 Self::open_locations_in_multibuffer(workspace, locations, replica_id, title, cx)
5489 }
5490 }
5491
5492 pub fn find_all_references(
5493 workspace: &mut Workspace,
5494 _: &FindAllReferences,
5495 cx: &mut ViewContext<Workspace>,
5496 ) -> Option<Task<Result<()>>> {
5497 let active_item = workspace.active_item(cx)?;
5498 let editor_handle = active_item.act_as::<Self>(cx)?;
5499
5500 let editor = editor_handle.read(cx);
5501 let buffer = editor.buffer.read(cx);
5502 let head = editor.selections.newest::<usize>(cx).head();
5503 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
5504 let replica_id = editor.replica_id(cx);
5505
5506 let project = workspace.project().clone();
5507 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
5508 Some(cx.spawn_labeled(
5509 "Finding All References...",
5510 |workspace, mut cx| async move {
5511 let locations = references.await?;
5512 if locations.is_empty() {
5513 return Ok(());
5514 }
5515
5516 workspace.update(&mut cx, |workspace, cx| {
5517 let title = locations
5518 .first()
5519 .as_ref()
5520 .map(|location| {
5521 let buffer = location.buffer.read(cx);
5522 format!(
5523 "References to `{}`",
5524 buffer
5525 .text_for_range(location.range.clone())
5526 .collect::<String>()
5527 )
5528 })
5529 .unwrap();
5530 Self::open_locations_in_multibuffer(
5531 workspace, locations, replica_id, title, cx,
5532 );
5533 });
5534
5535 Ok(())
5536 },
5537 ))
5538 }
5539
5540 /// Opens a multibuffer with the given project locations in it
5541 pub fn open_locations_in_multibuffer(
5542 workspace: &mut Workspace,
5543 mut locations: Vec<Location>,
5544 replica_id: ReplicaId,
5545 title: String,
5546 cx: &mut ViewContext<Workspace>,
5547 ) {
5548 // If there are multiple definitions, open them in a multibuffer
5549 locations.sort_by_key(|location| location.buffer.id());
5550 let mut locations = locations.into_iter().peekable();
5551 let mut ranges_to_highlight = Vec::new();
5552
5553 let excerpt_buffer = cx.add_model(|cx| {
5554 let mut multibuffer = MultiBuffer::new(replica_id);
5555 while let Some(location) = locations.next() {
5556 let buffer = location.buffer.read(cx);
5557 let mut ranges_for_buffer = Vec::new();
5558 let range = location.range.to_offset(buffer);
5559 ranges_for_buffer.push(range.clone());
5560
5561 while let Some(next_location) = locations.peek() {
5562 if next_location.buffer == location.buffer {
5563 ranges_for_buffer.push(next_location.range.to_offset(buffer));
5564 locations.next();
5565 } else {
5566 break;
5567 }
5568 }
5569
5570 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
5571 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
5572 location.buffer.clone(),
5573 ranges_for_buffer,
5574 1,
5575 cx,
5576 ))
5577 }
5578
5579 multibuffer.with_title(title)
5580 });
5581
5582 let editor = cx.add_view(|cx| {
5583 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
5584 });
5585 editor.update(cx, |editor, cx| {
5586 editor.highlight_background::<Self>(
5587 ranges_to_highlight,
5588 |theme| theme.editor.highlighted_line_background,
5589 cx,
5590 );
5591 });
5592 workspace.add_item(Box::new(editor), cx);
5593 }
5594
5595 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
5596 use language::ToOffset as _;
5597
5598 let project = self.project.clone()?;
5599 let selection = self.selections.newest_anchor().clone();
5600 let (cursor_buffer, cursor_buffer_position) = self
5601 .buffer
5602 .read(cx)
5603 .text_anchor_for_position(selection.head(), cx)?;
5604 let (tail_buffer, _) = self
5605 .buffer
5606 .read(cx)
5607 .text_anchor_for_position(selection.tail(), cx)?;
5608 if tail_buffer != cursor_buffer {
5609 return None;
5610 }
5611
5612 let snapshot = cursor_buffer.read(cx).snapshot();
5613 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
5614 let prepare_rename = project.update(cx, |project, cx| {
5615 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
5616 });
5617
5618 Some(cx.spawn(|this, mut cx| async move {
5619 let rename_range = if let Some(range) = prepare_rename.await? {
5620 Some(range)
5621 } else {
5622 this.read_with(&cx, |this, cx| {
5623 let buffer = this.buffer.read(cx).snapshot(cx);
5624 let mut buffer_highlights = this
5625 .document_highlights_for_position(selection.head(), &buffer)
5626 .filter(|highlight| {
5627 highlight.start.excerpt_id() == selection.head().excerpt_id()
5628 && highlight.end.excerpt_id() == selection.head().excerpt_id()
5629 });
5630 buffer_highlights
5631 .next()
5632 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
5633 })
5634 };
5635 if let Some(rename_range) = rename_range {
5636 let rename_buffer_range = rename_range.to_offset(&snapshot);
5637 let cursor_offset_in_rename_range =
5638 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
5639
5640 this.update(&mut cx, |this, cx| {
5641 this.take_rename(false, cx);
5642 let style = this.style(cx);
5643 let buffer = this.buffer.read(cx).read(cx);
5644 let cursor_offset = selection.head().to_offset(&buffer);
5645 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
5646 let rename_end = rename_start + rename_buffer_range.len();
5647 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
5648 let mut old_highlight_id = None;
5649 let old_name: Arc<str> = buffer
5650 .chunks(rename_start..rename_end, true)
5651 .map(|chunk| {
5652 if old_highlight_id.is_none() {
5653 old_highlight_id = chunk.syntax_highlight_id;
5654 }
5655 chunk.text
5656 })
5657 .collect::<String>()
5658 .into();
5659
5660 drop(buffer);
5661
5662 // Position the selection in the rename editor so that it matches the current selection.
5663 this.show_local_selections = false;
5664 let rename_editor = cx.add_view(|cx| {
5665 let mut editor = Editor::single_line(None, cx);
5666 if let Some(old_highlight_id) = old_highlight_id {
5667 editor.override_text_style =
5668 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
5669 }
5670 editor.buffer.update(cx, |buffer, cx| {
5671 buffer.edit([(0..0, old_name.clone())], None, cx)
5672 });
5673 editor.select_all(&SelectAll, cx);
5674 editor
5675 });
5676
5677 let ranges = this
5678 .clear_background_highlights::<DocumentHighlightWrite>(cx)
5679 .into_iter()
5680 .flat_map(|(_, ranges)| ranges)
5681 .chain(
5682 this.clear_background_highlights::<DocumentHighlightRead>(cx)
5683 .into_iter()
5684 .flat_map(|(_, ranges)| ranges),
5685 )
5686 .collect();
5687
5688 this.highlight_text::<Rename>(
5689 ranges,
5690 HighlightStyle {
5691 fade_out: Some(style.rename_fade),
5692 ..Default::default()
5693 },
5694 cx,
5695 );
5696 cx.focus(&rename_editor);
5697 let block_id = this.insert_blocks(
5698 [BlockProperties {
5699 style: BlockStyle::Flex,
5700 position: range.start.clone(),
5701 height: 1,
5702 render: Arc::new({
5703 let editor = rename_editor.clone();
5704 move |cx: &mut BlockContext| {
5705 ChildView::new(&editor, cx)
5706 .contained()
5707 .with_padding_left(cx.anchor_x)
5708 .boxed()
5709 }
5710 }),
5711 disposition: BlockDisposition::Below,
5712 }],
5713 cx,
5714 )[0];
5715 this.pending_rename = Some(RenameState {
5716 range,
5717 old_name,
5718 editor: rename_editor,
5719 block_id,
5720 });
5721 });
5722 }
5723
5724 Ok(())
5725 }))
5726 }
5727
5728 pub fn confirm_rename(
5729 workspace: &mut Workspace,
5730 _: &ConfirmRename,
5731 cx: &mut ViewContext<Workspace>,
5732 ) -> Option<Task<Result<()>>> {
5733 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
5734
5735 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
5736 let rename = editor.take_rename(false, cx)?;
5737 let buffer = editor.buffer.read(cx);
5738 let (start_buffer, start) =
5739 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
5740 let (end_buffer, end) =
5741 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
5742 if start_buffer == end_buffer {
5743 let new_name = rename.editor.read(cx).text(cx);
5744 Some((start_buffer, start..end, rename.old_name, new_name))
5745 } else {
5746 None
5747 }
5748 })?;
5749
5750 let rename = workspace.project().clone().update(cx, |project, cx| {
5751 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
5752 });
5753
5754 Some(cx.spawn(|workspace, mut cx| async move {
5755 let project_transaction = rename.await?;
5756 Self::open_project_transaction(
5757 editor.clone(),
5758 workspace,
5759 project_transaction,
5760 format!("Rename: {} → {}", old_name, new_name),
5761 cx.clone(),
5762 )
5763 .await?;
5764
5765 editor.update(&mut cx, |editor, cx| {
5766 editor.refresh_document_highlights(cx);
5767 });
5768 Ok(())
5769 }))
5770 }
5771
5772 fn take_rename(
5773 &mut self,
5774 moving_cursor: bool,
5775 cx: &mut ViewContext<Self>,
5776 ) -> Option<RenameState> {
5777 let rename = self.pending_rename.take()?;
5778 self.remove_blocks([rename.block_id].into_iter().collect(), cx);
5779 self.clear_text_highlights::<Rename>(cx);
5780 self.show_local_selections = true;
5781
5782 if moving_cursor {
5783 let rename_editor = rename.editor.read(cx);
5784 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
5785
5786 // Update the selection to match the position of the selection inside
5787 // the rename editor.
5788 let snapshot = self.buffer.read(cx).read(cx);
5789 let rename_range = rename.range.to_offset(&snapshot);
5790 let cursor_in_editor = snapshot
5791 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
5792 .min(rename_range.end);
5793 drop(snapshot);
5794
5795 self.change_selections(None, cx, |s| {
5796 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
5797 });
5798 } else {
5799 self.refresh_document_highlights(cx);
5800 }
5801
5802 Some(rename)
5803 }
5804
5805 #[cfg(any(test, feature = "test-support"))]
5806 pub fn pending_rename(&self) -> Option<&RenameState> {
5807 self.pending_rename.as_ref()
5808 }
5809
5810 fn format(&mut self, _: &Format, cx: &mut ViewContext<'_, Self>) -> Option<Task<Result<()>>> {
5811 let project = match &self.project {
5812 Some(project) => project.clone(),
5813 None => return None,
5814 };
5815
5816 Some(self.perform_format(project, FormatTrigger::Manual, cx))
5817 }
5818
5819 fn perform_format(
5820 &mut self,
5821 project: ModelHandle<Project>,
5822 trigger: FormatTrigger,
5823 cx: &mut ViewContext<'_, Self>,
5824 ) -> Task<Result<()>> {
5825 let buffer = self.buffer().clone();
5826 let buffers = buffer.read(cx).all_buffers();
5827
5828 let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
5829 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
5830
5831 cx.spawn(|_, mut cx| async move {
5832 let transaction = futures::select_biased! {
5833 _ = timeout => {
5834 log::warn!("timed out waiting for formatting");
5835 None
5836 }
5837 transaction = format.log_err().fuse() => transaction,
5838 };
5839
5840 buffer.update(&mut cx, |buffer, cx| {
5841 if let Some(transaction) = transaction {
5842 if !buffer.is_singleton() {
5843 buffer.push_transaction(&transaction.0);
5844 }
5845 }
5846
5847 cx.notify();
5848 });
5849
5850 Ok(())
5851 })
5852 }
5853
5854 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
5855 if let Some(project) = self.project.clone() {
5856 self.buffer.update(cx, |multi_buffer, cx| {
5857 project.update(cx, |project, cx| {
5858 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
5859 });
5860 })
5861 }
5862 }
5863
5864 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
5865 cx.show_character_palette();
5866 }
5867
5868 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
5869 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
5870 let buffer = self.buffer.read(cx).snapshot(cx);
5871 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
5872 let is_valid = buffer
5873 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
5874 .any(|entry| {
5875 entry.diagnostic.is_primary
5876 && !entry.range.is_empty()
5877 && entry.range.start == primary_range_start
5878 && entry.diagnostic.message == active_diagnostics.primary_message
5879 });
5880
5881 if is_valid != active_diagnostics.is_valid {
5882 active_diagnostics.is_valid = is_valid;
5883 let mut new_styles = HashMap::default();
5884 for (block_id, diagnostic) in &active_diagnostics.blocks {
5885 new_styles.insert(
5886 *block_id,
5887 diagnostic_block_renderer(diagnostic.clone(), is_valid),
5888 );
5889 }
5890 self.display_map
5891 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
5892 }
5893 }
5894 }
5895
5896 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
5897 self.dismiss_diagnostics(cx);
5898 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
5899 let buffer = self.buffer.read(cx).snapshot(cx);
5900
5901 let mut primary_range = None;
5902 let mut primary_message = None;
5903 let mut group_end = Point::zero();
5904 let diagnostic_group = buffer
5905 .diagnostic_group::<Point>(group_id)
5906 .map(|entry| {
5907 if entry.range.end > group_end {
5908 group_end = entry.range.end;
5909 }
5910 if entry.diagnostic.is_primary {
5911 primary_range = Some(entry.range.clone());
5912 primary_message = Some(entry.diagnostic.message.clone());
5913 }
5914 entry
5915 })
5916 .collect::<Vec<_>>();
5917 let primary_range = primary_range?;
5918 let primary_message = primary_message?;
5919 let primary_range =
5920 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
5921
5922 let blocks = display_map
5923 .insert_blocks(
5924 diagnostic_group.iter().map(|entry| {
5925 let diagnostic = entry.diagnostic.clone();
5926 let message_height = diagnostic.message.lines().count() as u8;
5927 BlockProperties {
5928 style: BlockStyle::Fixed,
5929 position: buffer.anchor_after(entry.range.start),
5930 height: message_height,
5931 render: diagnostic_block_renderer(diagnostic, true),
5932 disposition: BlockDisposition::Below,
5933 }
5934 }),
5935 cx,
5936 )
5937 .into_iter()
5938 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
5939 .collect();
5940
5941 Some(ActiveDiagnosticGroup {
5942 primary_range,
5943 primary_message,
5944 blocks,
5945 is_valid: true,
5946 })
5947 });
5948 self.active_diagnostics.is_some()
5949 }
5950
5951 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
5952 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
5953 self.display_map.update(cx, |display_map, cx| {
5954 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
5955 });
5956 cx.notify();
5957 }
5958 }
5959
5960 pub fn set_selections_from_remote(
5961 &mut self,
5962 selections: Vec<Selection<Anchor>>,
5963 pending_selection: Option<Selection<Anchor>>,
5964 cx: &mut ViewContext<Self>,
5965 ) {
5966 let old_cursor_position = self.selections.newest_anchor().head();
5967 self.selections.change_with(cx, |s| {
5968 s.select_anchors(selections);
5969 if let Some(pending_selection) = pending_selection {
5970 s.set_pending(pending_selection, SelectMode::Character);
5971 } else {
5972 s.clear_pending();
5973 }
5974 });
5975 self.selections_did_change(false, &old_cursor_position, cx);
5976 }
5977
5978 fn push_to_selection_history(&mut self) {
5979 self.selection_history.push(SelectionHistoryEntry {
5980 selections: self.selections.disjoint_anchors(),
5981 select_next_state: self.select_next_state.clone(),
5982 add_selections_state: self.add_selections_state.clone(),
5983 });
5984 }
5985
5986 pub fn transact(
5987 &mut self,
5988 cx: &mut ViewContext<Self>,
5989 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
5990 ) -> Option<TransactionId> {
5991 self.start_transaction_at(Instant::now(), cx);
5992 update(self, cx);
5993 self.end_transaction_at(Instant::now(), cx)
5994 }
5995
5996 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
5997 self.end_selection(cx);
5998 if let Some(tx_id) = self
5999 .buffer
6000 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
6001 {
6002 self.selection_history
6003 .insert_transaction(tx_id, self.selections.disjoint_anchors());
6004 }
6005 }
6006
6007 fn end_transaction_at(
6008 &mut self,
6009 now: Instant,
6010 cx: &mut ViewContext<Self>,
6011 ) -> Option<TransactionId> {
6012 if let Some(tx_id) = self
6013 .buffer
6014 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
6015 {
6016 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
6017 *end_selections = Some(self.selections.disjoint_anchors());
6018 } else {
6019 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
6020 }
6021
6022 cx.emit(Event::Edited);
6023 Some(tx_id)
6024 } else {
6025 None
6026 }
6027 }
6028
6029 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
6030 let mut fold_ranges = Vec::new();
6031
6032 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6033
6034 let selections = self.selections.all::<Point>(cx);
6035 for selection in selections {
6036 let range = selection.range().sorted();
6037 let buffer_start_row = range.start.row;
6038
6039 for row in (0..=range.end.row).rev() {
6040 let fold_range = display_map.foldable_range(row);
6041
6042 if let Some(fold_range) = fold_range {
6043 if fold_range.end.row >= buffer_start_row {
6044 fold_ranges.push(fold_range);
6045 if row <= range.start.row {
6046 break;
6047 }
6048 }
6049 }
6050 }
6051 }
6052
6053 self.fold_ranges(fold_ranges, true, cx);
6054 }
6055
6056 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
6057 let buffer_row = fold_at.buffer_row;
6058 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6059
6060 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
6061 let autoscroll = self
6062 .selections
6063 .all::<Point>(cx)
6064 .iter()
6065 .any(|selection| fold_range.overlaps(&selection.range()));
6066
6067 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
6068 }
6069 }
6070
6071 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
6072 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6073 let buffer = &display_map.buffer_snapshot;
6074 let selections = self.selections.all::<Point>(cx);
6075 let ranges = selections
6076 .iter()
6077 .map(|s| {
6078 let range = s.display_range(&display_map).sorted();
6079 let mut start = range.start.to_point(&display_map);
6080 let mut end = range.end.to_point(&display_map);
6081 start.column = 0;
6082 end.column = buffer.line_len(end.row);
6083 start..end
6084 })
6085 .collect::<Vec<_>>();
6086
6087 self.unfold_ranges(ranges, true, true, cx);
6088 }
6089
6090 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
6091 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6092
6093 let intersection_range = Point::new(unfold_at.buffer_row, 0)
6094 ..Point::new(
6095 unfold_at.buffer_row,
6096 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
6097 );
6098
6099 let autoscroll = self
6100 .selections
6101 .all::<Point>(cx)
6102 .iter()
6103 .any(|selection| selection.range().overlaps(&intersection_range));
6104
6105 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
6106 }
6107
6108 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
6109 let selections = self.selections.all::<Point>(cx);
6110 let ranges = selections.into_iter().map(|s| s.start..s.end);
6111 self.fold_ranges(ranges, true, cx);
6112 }
6113
6114 pub fn fold_ranges<T: ToOffset + Clone>(
6115 &mut self,
6116 ranges: impl IntoIterator<Item = Range<T>>,
6117 auto_scroll: bool,
6118 cx: &mut ViewContext<Self>,
6119 ) {
6120 let mut ranges = ranges.into_iter().peekable();
6121 if ranges.peek().is_some() {
6122 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
6123
6124 if auto_scroll {
6125 self.request_autoscroll(Autoscroll::fit(), cx);
6126 }
6127
6128 cx.notify();
6129 }
6130 }
6131
6132 pub fn unfold_ranges<T: ToOffset + Clone>(
6133 &mut self,
6134 ranges: impl IntoIterator<Item = Range<T>>,
6135 inclusive: bool,
6136 auto_scroll: bool,
6137 cx: &mut ViewContext<Self>,
6138 ) {
6139 let mut ranges = ranges.into_iter().peekable();
6140 if ranges.peek().is_some() {
6141 self.display_map
6142 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
6143 if auto_scroll {
6144 self.request_autoscroll(Autoscroll::fit(), cx);
6145 }
6146
6147 cx.notify();
6148 }
6149 }
6150
6151 pub fn gutter_hover(
6152 &mut self,
6153 GutterHover { hovered }: &GutterHover,
6154 cx: &mut ViewContext<Self>,
6155 ) {
6156 self.gutter_hovered = *hovered;
6157 cx.notify();
6158 }
6159
6160 pub fn insert_blocks(
6161 &mut self,
6162 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
6163 cx: &mut ViewContext<Self>,
6164 ) -> Vec<BlockId> {
6165 let blocks = self
6166 .display_map
6167 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
6168 self.request_autoscroll(Autoscroll::fit(), cx);
6169 blocks
6170 }
6171
6172 pub fn replace_blocks(
6173 &mut self,
6174 blocks: HashMap<BlockId, RenderBlock>,
6175 cx: &mut ViewContext<Self>,
6176 ) {
6177 self.display_map
6178 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
6179 self.request_autoscroll(Autoscroll::fit(), cx);
6180 }
6181
6182 pub fn remove_blocks(&mut self, block_ids: HashSet<BlockId>, cx: &mut ViewContext<Self>) {
6183 self.display_map.update(cx, |display_map, cx| {
6184 display_map.remove_blocks(block_ids, cx)
6185 });
6186 }
6187
6188 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
6189 self.display_map
6190 .update(cx, |map, cx| map.snapshot(cx))
6191 .longest_row()
6192 }
6193
6194 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
6195 self.display_map
6196 .update(cx, |map, cx| map.snapshot(cx))
6197 .max_point()
6198 }
6199
6200 pub fn text(&self, cx: &AppContext) -> String {
6201 self.buffer.read(cx).read(cx).text()
6202 }
6203
6204 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
6205 self.transact(cx, |this, cx| {
6206 this.buffer
6207 .read(cx)
6208 .as_singleton()
6209 .expect("you can only call set_text on editors for singleton buffers")
6210 .update(cx, |buffer, cx| buffer.set_text(text, cx));
6211 });
6212 }
6213
6214 pub fn display_text(&self, cx: &mut AppContext) -> String {
6215 self.display_map
6216 .update(cx, |map, cx| map.snapshot(cx))
6217 .text()
6218 }
6219
6220 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
6221 let language_name = self
6222 .buffer
6223 .read(cx)
6224 .as_singleton()
6225 .and_then(|singleton_buffer| singleton_buffer.read(cx).language())
6226 .map(|l| l.name());
6227
6228 let settings = cx.global::<Settings>();
6229 let mode = self
6230 .soft_wrap_mode_override
6231 .unwrap_or_else(|| settings.soft_wrap(language_name.as_deref()));
6232 match mode {
6233 settings::SoftWrap::None => SoftWrap::None,
6234 settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
6235 settings::SoftWrap::PreferredLineLength => {
6236 SoftWrap::Column(settings.preferred_line_length(language_name.as_deref()))
6237 }
6238 }
6239 }
6240
6241 pub fn set_soft_wrap_mode(&mut self, mode: settings::SoftWrap, cx: &mut ViewContext<Self>) {
6242 self.soft_wrap_mode_override = Some(mode);
6243 cx.notify();
6244 }
6245
6246 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut AppContext) -> bool {
6247 self.display_map
6248 .update(cx, |map, cx| map.set_wrap_width(width, cx))
6249 }
6250
6251 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
6252 if self.soft_wrap_mode_override.is_some() {
6253 self.soft_wrap_mode_override.take();
6254 } else {
6255 let soft_wrap = match self.soft_wrap_mode(cx) {
6256 SoftWrap::None => settings::SoftWrap::EditorWidth,
6257 SoftWrap::EditorWidth | SoftWrap::Column(_) => settings::SoftWrap::None,
6258 };
6259 self.soft_wrap_mode_override = Some(soft_wrap);
6260 }
6261 cx.notify();
6262 }
6263
6264 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
6265 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6266 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6267 cx.reveal_path(&file.abs_path(cx));
6268 }
6269 }
6270 }
6271
6272 pub fn copy_path(&mut self, _: &CopyPath, 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 if let Some(path) = file.abs_path(cx).to_str() {
6276 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
6277 }
6278 }
6279 }
6280 }
6281
6282 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
6283 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6284 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6285 if let Some(path) = file.path().to_str() {
6286 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
6287 }
6288 }
6289 }
6290 }
6291
6292 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
6293 self.highlighted_rows = rows;
6294 }
6295
6296 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
6297 self.highlighted_rows.clone()
6298 }
6299
6300 pub fn highlight_background<T: 'static>(
6301 &mut self,
6302 ranges: Vec<Range<Anchor>>,
6303 color_fetcher: fn(&Theme) -> Color,
6304 cx: &mut ViewContext<Self>,
6305 ) {
6306 self.background_highlights
6307 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
6308 cx.notify();
6309 }
6310
6311 #[allow(clippy::type_complexity)]
6312 pub fn clear_background_highlights<T: 'static>(
6313 &mut self,
6314 cx: &mut ViewContext<Self>,
6315 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
6316 let highlights = self.background_highlights.remove(&TypeId::of::<T>());
6317 if highlights.is_some() {
6318 cx.notify();
6319 }
6320 highlights
6321 }
6322
6323 #[cfg(feature = "test-support")]
6324 pub fn all_background_highlights(
6325 &mut self,
6326 cx: &mut ViewContext<Self>,
6327 ) -> Vec<(Range<DisplayPoint>, Color)> {
6328 let snapshot = self.snapshot(cx);
6329 let buffer = &snapshot.buffer_snapshot;
6330 let start = buffer.anchor_before(0);
6331 let end = buffer.anchor_after(buffer.len());
6332 let theme = cx.global::<Settings>().theme.as_ref();
6333 self.background_highlights_in_range(start..end, &snapshot, theme)
6334 }
6335
6336 fn document_highlights_for_position<'a>(
6337 &'a self,
6338 position: Anchor,
6339 buffer: &'a MultiBufferSnapshot,
6340 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
6341 let read_highlights = self
6342 .background_highlights
6343 .get(&TypeId::of::<DocumentHighlightRead>())
6344 .map(|h| &h.1);
6345 let write_highlights = self
6346 .background_highlights
6347 .get(&TypeId::of::<DocumentHighlightWrite>())
6348 .map(|h| &h.1);
6349 let left_position = position.bias_left(buffer);
6350 let right_position = position.bias_right(buffer);
6351 read_highlights
6352 .into_iter()
6353 .chain(write_highlights)
6354 .flat_map(move |ranges| {
6355 let start_ix = match ranges.binary_search_by(|probe| {
6356 let cmp = probe.end.cmp(&left_position, buffer);
6357 if cmp.is_ge() {
6358 Ordering::Greater
6359 } else {
6360 Ordering::Less
6361 }
6362 }) {
6363 Ok(i) | Err(i) => i,
6364 };
6365
6366 let right_position = right_position.clone();
6367 ranges[start_ix..]
6368 .iter()
6369 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
6370 })
6371 }
6372
6373 pub fn background_highlights_in_range(
6374 &self,
6375 search_range: Range<Anchor>,
6376 display_snapshot: &DisplaySnapshot,
6377 theme: &Theme,
6378 ) -> Vec<(Range<DisplayPoint>, Color)> {
6379 let mut results = Vec::new();
6380 let buffer = &display_snapshot.buffer_snapshot;
6381 for (color_fetcher, ranges) in self.background_highlights.values() {
6382 let color = color_fetcher(theme);
6383 let start_ix = match ranges.binary_search_by(|probe| {
6384 let cmp = probe.end.cmp(&search_range.start, buffer);
6385 if cmp.is_gt() {
6386 Ordering::Greater
6387 } else {
6388 Ordering::Less
6389 }
6390 }) {
6391 Ok(i) | Err(i) => i,
6392 };
6393 for range in &ranges[start_ix..] {
6394 if range.start.cmp(&search_range.end, buffer).is_ge() {
6395 break;
6396 }
6397 let start = range
6398 .start
6399 .to_point(buffer)
6400 .to_display_point(display_snapshot);
6401 let end = range
6402 .end
6403 .to_point(buffer)
6404 .to_display_point(display_snapshot);
6405 results.push((start..end, color))
6406 }
6407 }
6408 results
6409 }
6410
6411 pub fn highlight_text<T: 'static>(
6412 &mut self,
6413 ranges: Vec<Range<Anchor>>,
6414 style: HighlightStyle,
6415 cx: &mut ViewContext<Self>,
6416 ) {
6417 self.display_map.update(cx, |map, _| {
6418 map.highlight_text(TypeId::of::<T>(), ranges, style)
6419 });
6420 cx.notify();
6421 }
6422
6423 pub fn text_highlights<'a, T: 'static>(
6424 &'a self,
6425 cx: &'a AppContext,
6426 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
6427 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
6428 }
6429
6430 pub fn clear_text_highlights<T: 'static>(
6431 &mut self,
6432 cx: &mut ViewContext<Self>,
6433 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
6434 let highlights = self
6435 .display_map
6436 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()));
6437 if highlights.is_some() {
6438 cx.notify();
6439 }
6440 highlights
6441 }
6442
6443 pub fn show_local_cursors(&self, cx: &AppContext) -> bool {
6444 self.blink_manager.read(cx).visible() && self.focused
6445 }
6446
6447 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
6448 cx.notify();
6449 }
6450
6451 fn on_buffer_event(
6452 &mut self,
6453 _: ModelHandle<MultiBuffer>,
6454 event: &multi_buffer::Event,
6455 cx: &mut ViewContext<Self>,
6456 ) {
6457 match event {
6458 multi_buffer::Event::Edited => {
6459 self.refresh_active_diagnostics(cx);
6460 self.refresh_code_actions(cx);
6461 if self.has_active_copilot_suggestion(cx) {
6462 self.update_visible_copilot_suggestion(cx);
6463 }
6464 cx.emit(Event::BufferEdited);
6465 }
6466 multi_buffer::Event::ExcerptsAdded {
6467 buffer,
6468 predecessor,
6469 excerpts,
6470 } => cx.emit(Event::ExcerptsAdded {
6471 buffer: buffer.clone(),
6472 predecessor: *predecessor,
6473 excerpts: excerpts.clone(),
6474 }),
6475 multi_buffer::Event::ExcerptsRemoved { ids } => {
6476 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
6477 }
6478 multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed),
6479 multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
6480 multi_buffer::Event::Saved => cx.emit(Event::Saved),
6481 multi_buffer::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
6482 multi_buffer::Event::Reloaded => cx.emit(Event::TitleChanged),
6483 multi_buffer::Event::Closed => cx.emit(Event::Closed),
6484 multi_buffer::Event::DiagnosticsUpdated => {
6485 self.refresh_active_diagnostics(cx);
6486 }
6487 }
6488 }
6489
6490 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
6491 cx.notify();
6492 }
6493
6494 fn on_settings_changed(&mut self, cx: &mut ViewContext<Self>) {
6495 self.refresh_copilot_suggestions(cx);
6496 }
6497
6498 pub fn set_searchable(&mut self, searchable: bool) {
6499 self.searchable = searchable;
6500 }
6501
6502 pub fn searchable(&self) -> bool {
6503 self.searchable
6504 }
6505
6506 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
6507 let active_item = workspace.active_item(cx);
6508 let editor_handle = if let Some(editor) = active_item
6509 .as_ref()
6510 .and_then(|item| item.act_as::<Self>(cx))
6511 {
6512 editor
6513 } else {
6514 cx.propagate_action();
6515 return;
6516 };
6517
6518 let editor = editor_handle.read(cx);
6519 let buffer = editor.buffer.read(cx);
6520 if buffer.is_singleton() {
6521 cx.propagate_action();
6522 return;
6523 }
6524
6525 let mut new_selections_by_buffer = HashMap::default();
6526 for selection in editor.selections.all::<usize>(cx) {
6527 for (buffer, mut range) in
6528 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
6529 {
6530 if selection.reversed {
6531 mem::swap(&mut range.start, &mut range.end);
6532 }
6533 new_selections_by_buffer
6534 .entry(buffer)
6535 .or_insert(Vec::new())
6536 .push(range)
6537 }
6538 }
6539
6540 editor_handle.update(cx, |editor, cx| {
6541 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
6542 });
6543 let pane = workspace.active_pane().clone();
6544 pane.update(cx, |pane, _| pane.disable_history());
6545
6546 // We defer the pane interaction because we ourselves are a workspace item
6547 // and activating a new item causes the pane to call a method on us reentrantly,
6548 // which panics if we're on the stack.
6549 cx.defer(move |workspace, cx| {
6550 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
6551 let editor = workspace.open_project_item::<Self>(buffer, cx);
6552 editor.update(cx, |editor, cx| {
6553 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
6554 s.select_ranges(ranges);
6555 });
6556 });
6557 }
6558
6559 pane.update(cx, |pane, _| pane.enable_history());
6560 });
6561 }
6562
6563 fn jump(workspace: &mut Workspace, action: &Jump, cx: &mut ViewContext<Workspace>) {
6564 let editor = workspace.open_path(action.path.clone(), None, true, cx);
6565 let position = action.position;
6566 let anchor = action.anchor;
6567 cx.spawn_weak(|_, mut cx| async move {
6568 let editor = editor.await.log_err()?.downcast::<Editor>()?;
6569 editor.update(&mut cx, |editor, cx| {
6570 let buffer = editor.buffer().read(cx).as_singleton()?;
6571 let buffer = buffer.read(cx);
6572 let cursor = if buffer.can_resolve(&anchor) {
6573 language::ToPoint::to_point(&anchor, buffer)
6574 } else {
6575 buffer.clip_point(position, Bias::Left)
6576 };
6577
6578 let nav_history = editor.nav_history.take();
6579 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
6580 s.select_ranges([cursor..cursor]);
6581 });
6582 editor.nav_history = nav_history;
6583
6584 Some(())
6585 })?;
6586 Some(())
6587 })
6588 .detach()
6589 }
6590
6591 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
6592 let snapshot = self.buffer.read(cx).read(cx);
6593 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
6594 Some(
6595 ranges
6596 .iter()
6597 .map(move |range| {
6598 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
6599 })
6600 .collect(),
6601 )
6602 }
6603
6604 fn selection_replacement_ranges(
6605 &self,
6606 range: Range<OffsetUtf16>,
6607 cx: &AppContext,
6608 ) -> Vec<Range<OffsetUtf16>> {
6609 let selections = self.selections.all::<OffsetUtf16>(cx);
6610 let newest_selection = selections
6611 .iter()
6612 .max_by_key(|selection| selection.id)
6613 .unwrap();
6614 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
6615 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
6616 let snapshot = self.buffer.read(cx).read(cx);
6617 selections
6618 .into_iter()
6619 .map(|mut selection| {
6620 selection.start.0 =
6621 (selection.start.0 as isize).saturating_add(start_delta) as usize;
6622 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
6623 snapshot.clip_offset_utf16(selection.start, Bias::Left)
6624 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
6625 })
6626 .collect()
6627 }
6628
6629 fn report_event(&self, name: &str, cx: &AppContext) {
6630 if let Some((project, file)) = self.project.as_ref().zip(
6631 self.buffer
6632 .read(cx)
6633 .as_singleton()
6634 .and_then(|b| b.read(cx).file()),
6635 ) {
6636 let settings = cx.global::<Settings>();
6637
6638 let extension = Path::new(file.file_name(cx))
6639 .extension()
6640 .and_then(|e| e.to_str());
6641 project.read(cx).client().report_event(
6642 name,
6643 json!({ "File Extension": extension, "Vim Mode": settings.vim_mode }),
6644 settings.telemetry(),
6645 );
6646 }
6647 }
6648
6649 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
6650 /// with each line being an array of {text, highlight} objects.
6651 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
6652 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
6653 return;
6654 };
6655
6656 #[derive(Serialize)]
6657 struct Chunk<'a> {
6658 text: String,
6659 highlight: Option<&'a str>,
6660 }
6661
6662 let snapshot = buffer.read(cx).snapshot();
6663 let range = self
6664 .selected_text_range(cx)
6665 .and_then(|selected_range| {
6666 if selected_range.is_empty() {
6667 None
6668 } else {
6669 Some(selected_range)
6670 }
6671 })
6672 .unwrap_or_else(|| 0..snapshot.len());
6673
6674 let chunks = snapshot.chunks(range, true);
6675 let mut lines = Vec::new();
6676 let mut line: VecDeque<Chunk> = VecDeque::new();
6677
6678 let theme = &cx.global::<Settings>().theme.editor.syntax;
6679
6680 for chunk in chunks {
6681 let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme));
6682 let mut chunk_lines = chunk.text.split("\n").peekable();
6683 while let Some(text) = chunk_lines.next() {
6684 let mut merged_with_last_token = false;
6685 if let Some(last_token) = line.back_mut() {
6686 if last_token.highlight == highlight {
6687 last_token.text.push_str(text);
6688 merged_with_last_token = true;
6689 }
6690 }
6691
6692 if !merged_with_last_token {
6693 line.push_back(Chunk {
6694 text: text.into(),
6695 highlight,
6696 });
6697 }
6698
6699 if chunk_lines.peek().is_some() {
6700 if line.len() > 1 && line.front().unwrap().text.is_empty() {
6701 line.pop_front();
6702 }
6703 if line.len() > 1 && line.back().unwrap().text.is_empty() {
6704 line.pop_back();
6705 }
6706
6707 lines.push(mem::take(&mut line));
6708 }
6709 }
6710 }
6711
6712 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else { return; };
6713 cx.write_to_clipboard(ClipboardItem::new(lines));
6714 }
6715}
6716
6717fn consume_contiguous_rows(
6718 contiguous_row_selections: &mut Vec<Selection<Point>>,
6719 selection: &Selection<Point>,
6720 display_map: &DisplaySnapshot,
6721 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
6722) -> (u32, u32) {
6723 contiguous_row_selections.push(selection.clone());
6724 let start_row = selection.start.row;
6725 let mut end_row = ending_row(selection, display_map);
6726
6727 while let Some(next_selection) = selections.peek() {
6728 if next_selection.start.row <= end_row {
6729 end_row = ending_row(next_selection, display_map);
6730 contiguous_row_selections.push(selections.next().unwrap().clone());
6731 } else {
6732 break;
6733 }
6734 }
6735 (start_row, end_row)
6736}
6737
6738fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
6739 if next_selection.end.column > 0 || next_selection.is_empty() {
6740 display_map.next_line_boundary(next_selection.end).0.row + 1
6741 } else {
6742 next_selection.end.row
6743 }
6744}
6745
6746impl EditorSnapshot {
6747 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
6748 self.display_snapshot.buffer_snapshot.language_at(position)
6749 }
6750
6751 pub fn is_focused(&self) -> bool {
6752 self.is_focused
6753 }
6754
6755 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
6756 self.placeholder_text.as_ref()
6757 }
6758
6759 pub fn scroll_position(&self) -> Vector2F {
6760 self.scroll_anchor.scroll_position(&self.display_snapshot)
6761 }
6762}
6763
6764impl Deref for EditorSnapshot {
6765 type Target = DisplaySnapshot;
6766
6767 fn deref(&self) -> &Self::Target {
6768 &self.display_snapshot
6769 }
6770}
6771
6772#[derive(Clone, Debug, PartialEq, Eq)]
6773pub enum Event {
6774 InputIgnored {
6775 text: Arc<str>,
6776 },
6777 ExcerptsAdded {
6778 buffer: ModelHandle<Buffer>,
6779 predecessor: ExcerptId,
6780 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
6781 },
6782 ExcerptsRemoved {
6783 ids: Vec<ExcerptId>,
6784 },
6785 BufferEdited,
6786 Edited,
6787 Reparsed,
6788 Blurred,
6789 DirtyChanged,
6790 Saved,
6791 TitleChanged,
6792 SelectionsChanged {
6793 local: bool,
6794 },
6795 ScrollPositionChanged {
6796 local: bool,
6797 },
6798 Closed,
6799}
6800
6801pub struct EditorFocused(pub ViewHandle<Editor>);
6802pub struct EditorBlurred(pub ViewHandle<Editor>);
6803pub struct EditorReleased(pub WeakViewHandle<Editor>);
6804
6805impl Entity for Editor {
6806 type Event = Event;
6807
6808 fn release(&mut self, cx: &mut AppContext) {
6809 cx.emit_global(EditorReleased(self.handle.clone()));
6810 }
6811}
6812
6813impl View for Editor {
6814 fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
6815 let style = self.style(cx);
6816 let font_changed = self.display_map.update(cx, |map, cx| {
6817 map.set_fold_ellipses_color(style.folds.ellipses.text_color);
6818 map.set_font(style.text.font_id, style.text.font_size, cx)
6819 });
6820
6821 if font_changed {
6822 let handle = self.handle.clone();
6823 cx.defer(move |cx| {
6824 if let Some(editor) = handle.upgrade(cx) {
6825 editor.update(cx, |editor, cx| {
6826 hide_hover(editor, &HideHover, cx);
6827 hide_link_definition(editor, cx);
6828 })
6829 }
6830 });
6831 }
6832
6833 Stack::new()
6834 .with_child(EditorElement::new(self.handle.clone(), style.clone()).boxed())
6835 .with_child(ChildView::new(&self.mouse_context_menu, cx).boxed())
6836 .boxed()
6837 }
6838
6839 fn ui_name() -> &'static str {
6840 "Editor"
6841 }
6842
6843 fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
6844 if cx.is_self_focused() {
6845 let focused_event = EditorFocused(cx.handle());
6846 cx.emit_global(focused_event);
6847 }
6848 if let Some(rename) = self.pending_rename.as_ref() {
6849 cx.focus(&rename.editor);
6850 } else {
6851 if !self.focused {
6852 self.blink_manager.update(cx, BlinkManager::enable);
6853 }
6854 self.focused = true;
6855 self.buffer.update(cx, |buffer, cx| {
6856 buffer.finalize_last_transaction(cx);
6857 if self.leader_replica_id.is_none() {
6858 buffer.set_active_selections(
6859 &self.selections.disjoint_anchors(),
6860 self.selections.line_mode,
6861 self.cursor_shape,
6862 cx,
6863 );
6864 }
6865 });
6866 }
6867 }
6868
6869 fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
6870 let blurred_event = EditorBlurred(cx.handle());
6871 cx.emit_global(blurred_event);
6872 self.focused = false;
6873 self.blink_manager.update(cx, BlinkManager::disable);
6874 self.buffer
6875 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
6876 self.hide_context_menu(cx);
6877 hide_hover(self, &HideHover, cx);
6878 cx.emit(Event::Blurred);
6879 cx.notify();
6880 }
6881
6882 fn modifiers_changed(
6883 &mut self,
6884 event: &gpui::platform::ModifiersChangedEvent,
6885 cx: &mut ViewContext<Self>,
6886 ) -> bool {
6887 let pending_selection = self.has_pending_selection();
6888
6889 if let Some(point) = self.link_go_to_definition_state.last_mouse_location.clone() {
6890 if event.cmd && !pending_selection {
6891 let snapshot = self.snapshot(cx);
6892 let kind = if event.shift {
6893 LinkDefinitionKind::Type
6894 } else {
6895 LinkDefinitionKind::Symbol
6896 };
6897
6898 show_link_definition(kind, self, point, snapshot, cx);
6899 return false;
6900 }
6901 }
6902
6903 {
6904 if self.link_go_to_definition_state.symbol_range.is_some()
6905 || !self.link_go_to_definition_state.definitions.is_empty()
6906 {
6907 self.link_go_to_definition_state.symbol_range.take();
6908 self.link_go_to_definition_state.definitions.clear();
6909 cx.notify();
6910 }
6911
6912 self.link_go_to_definition_state.task = None;
6913
6914 self.clear_text_highlights::<LinkGoToDefinitionState>(cx);
6915 }
6916
6917 false
6918 }
6919
6920 fn keymap_context(&self, _: &AppContext) -> KeymapContext {
6921 let mut context = Self::default_keymap_context();
6922 let mode = match self.mode {
6923 EditorMode::SingleLine => "single_line",
6924 EditorMode::AutoHeight { .. } => "auto_height",
6925 EditorMode::Full => "full",
6926 };
6927 context.add_key("mode", mode);
6928 if self.pending_rename.is_some() {
6929 context.add_identifier("renaming");
6930 }
6931 match self.context_menu.as_ref() {
6932 Some(ContextMenu::Completions(_)) => context.add_identifier("showing_completions"),
6933 Some(ContextMenu::CodeActions(_)) => context.add_identifier("showing_code_actions"),
6934 None => {}
6935 }
6936
6937 for layer in self.keymap_context_layers.values() {
6938 context.extend(layer);
6939 }
6940
6941 context
6942 }
6943
6944 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
6945 Some(
6946 self.buffer
6947 .read(cx)
6948 .read(cx)
6949 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
6950 .collect(),
6951 )
6952 }
6953
6954 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
6955 // Prevent the IME menu from appearing when holding down an alphabetic key
6956 // while input is disabled.
6957 if !self.input_enabled {
6958 return None;
6959 }
6960
6961 let range = self.selections.newest::<OffsetUtf16>(cx).range();
6962 Some(range.start.0..range.end.0)
6963 }
6964
6965 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
6966 let snapshot = self.buffer.read(cx).read(cx);
6967 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
6968 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
6969 }
6970
6971 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
6972 self.clear_text_highlights::<InputComposition>(cx);
6973 self.ime_transaction.take();
6974 }
6975
6976 fn replace_text_in_range(
6977 &mut self,
6978 range_utf16: Option<Range<usize>>,
6979 text: &str,
6980 cx: &mut ViewContext<Self>,
6981 ) {
6982 self.transact(cx, |this, cx| {
6983 if this.input_enabled {
6984 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
6985 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
6986 Some(this.selection_replacement_ranges(range_utf16, cx))
6987 } else {
6988 this.marked_text_ranges(cx)
6989 };
6990
6991 if let Some(new_selected_ranges) = new_selected_ranges {
6992 this.change_selections(None, cx, |selections| {
6993 selections.select_ranges(new_selected_ranges)
6994 });
6995 }
6996 }
6997
6998 this.handle_input(text, cx);
6999 });
7000
7001 if !self.input_enabled {
7002 return;
7003 }
7004
7005 if let Some(transaction) = self.ime_transaction {
7006 self.buffer.update(cx, |buffer, cx| {
7007 buffer.group_until_transaction(transaction, cx);
7008 });
7009 }
7010
7011 self.unmark_text(cx);
7012 }
7013
7014 fn replace_and_mark_text_in_range(
7015 &mut self,
7016 range_utf16: Option<Range<usize>>,
7017 text: &str,
7018 new_selected_range_utf16: Option<Range<usize>>,
7019 cx: &mut ViewContext<Self>,
7020 ) {
7021 if !self.input_enabled {
7022 return;
7023 }
7024
7025 let transaction = self.transact(cx, |this, cx| {
7026 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
7027 let snapshot = this.buffer.read(cx).read(cx);
7028 if let Some(relative_range_utf16) = range_utf16.as_ref() {
7029 for marked_range in &mut marked_ranges {
7030 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
7031 marked_range.start.0 += relative_range_utf16.start;
7032 marked_range.start =
7033 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
7034 marked_range.end =
7035 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
7036 }
7037 }
7038 Some(marked_ranges)
7039 } else if let Some(range_utf16) = range_utf16 {
7040 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
7041 Some(this.selection_replacement_ranges(range_utf16, cx))
7042 } else {
7043 None
7044 };
7045
7046 if let Some(ranges) = ranges_to_replace {
7047 this.change_selections(None, cx, |s| s.select_ranges(ranges));
7048 }
7049
7050 let marked_ranges = {
7051 let snapshot = this.buffer.read(cx).read(cx);
7052 this.selections
7053 .disjoint_anchors()
7054 .iter()
7055 .map(|selection| {
7056 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
7057 })
7058 .collect::<Vec<_>>()
7059 };
7060
7061 if text.is_empty() {
7062 this.unmark_text(cx);
7063 } else {
7064 this.highlight_text::<InputComposition>(
7065 marked_ranges.clone(),
7066 this.style(cx).composition_mark,
7067 cx,
7068 );
7069 }
7070
7071 this.handle_input(text, cx);
7072
7073 if let Some(new_selected_range) = new_selected_range_utf16 {
7074 let snapshot = this.buffer.read(cx).read(cx);
7075 let new_selected_ranges = marked_ranges
7076 .into_iter()
7077 .map(|marked_range| {
7078 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
7079 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
7080 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
7081 snapshot.clip_offset_utf16(new_start, Bias::Left)
7082 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
7083 })
7084 .collect::<Vec<_>>();
7085
7086 drop(snapshot);
7087 this.change_selections(None, cx, |selections| {
7088 selections.select_ranges(new_selected_ranges)
7089 });
7090 }
7091 });
7092
7093 self.ime_transaction = self.ime_transaction.or(transaction);
7094 if let Some(transaction) = self.ime_transaction {
7095 self.buffer.update(cx, |buffer, cx| {
7096 buffer.group_until_transaction(transaction, cx);
7097 });
7098 }
7099
7100 if self.text_highlights::<InputComposition>(cx).is_none() {
7101 self.ime_transaction.take();
7102 }
7103 }
7104}
7105
7106fn build_style(
7107 settings: &Settings,
7108 get_field_editor_theme: Option<&GetFieldEditorTheme>,
7109 override_text_style: Option<&OverrideTextStyle>,
7110 cx: &AppContext,
7111) -> EditorStyle {
7112 let font_cache = cx.font_cache();
7113
7114 let mut theme = settings.theme.editor.clone();
7115 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
7116 let field_editor_theme = get_field_editor_theme(&settings.theme);
7117 theme.text_color = field_editor_theme.text.color;
7118 theme.selection = field_editor_theme.selection;
7119 theme.background = field_editor_theme
7120 .container
7121 .background_color
7122 .unwrap_or_default();
7123 EditorStyle {
7124 text: field_editor_theme.text,
7125 placeholder_text: field_editor_theme.placeholder_text,
7126 theme,
7127 }
7128 } else {
7129 let font_family_id = settings.buffer_font_family;
7130 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
7131 let font_properties = Default::default();
7132 let font_id = font_cache
7133 .select_font(font_family_id, &font_properties)
7134 .unwrap();
7135 let font_size = settings.buffer_font_size;
7136 EditorStyle {
7137 text: TextStyle {
7138 color: settings.theme.editor.text_color,
7139 font_family_name,
7140 font_family_id,
7141 font_id,
7142 font_size,
7143 font_properties,
7144 underline: Default::default(),
7145 },
7146 placeholder_text: None,
7147 theme,
7148 }
7149 };
7150
7151 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
7152 if let Some(highlighted) = style
7153 .text
7154 .clone()
7155 .highlight(highlight_style, font_cache)
7156 .log_err()
7157 {
7158 style.text = highlighted;
7159 }
7160 }
7161
7162 style
7163}
7164
7165trait SelectionExt {
7166 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
7167 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
7168 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
7169 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
7170 -> Range<u32>;
7171}
7172
7173impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
7174 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
7175 let start = self.start.to_point(buffer);
7176 let end = self.end.to_point(buffer);
7177 if self.reversed {
7178 end..start
7179 } else {
7180 start..end
7181 }
7182 }
7183
7184 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
7185 let start = self.start.to_offset(buffer);
7186 let end = self.end.to_offset(buffer);
7187 if self.reversed {
7188 end..start
7189 } else {
7190 start..end
7191 }
7192 }
7193
7194 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
7195 let start = self
7196 .start
7197 .to_point(&map.buffer_snapshot)
7198 .to_display_point(map);
7199 let end = self
7200 .end
7201 .to_point(&map.buffer_snapshot)
7202 .to_display_point(map);
7203 if self.reversed {
7204 end..start
7205 } else {
7206 start..end
7207 }
7208 }
7209
7210 fn spanned_rows(
7211 &self,
7212 include_end_if_at_line_start: bool,
7213 map: &DisplaySnapshot,
7214 ) -> Range<u32> {
7215 let start = self.start.to_point(&map.buffer_snapshot);
7216 let mut end = self.end.to_point(&map.buffer_snapshot);
7217 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
7218 end.row -= 1;
7219 }
7220
7221 let buffer_start = map.prev_line_boundary(start).0;
7222 let buffer_end = map.next_line_boundary(end).0;
7223 buffer_start.row..buffer_end.row + 1
7224 }
7225}
7226
7227impl<T: InvalidationRegion> InvalidationStack<T> {
7228 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
7229 where
7230 S: Clone + ToOffset,
7231 {
7232 while let Some(region) = self.last() {
7233 let all_selections_inside_invalidation_ranges =
7234 if selections.len() == region.ranges().len() {
7235 selections
7236 .iter()
7237 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
7238 .all(|(selection, invalidation_range)| {
7239 let head = selection.head().to_offset(buffer);
7240 invalidation_range.start <= head && invalidation_range.end >= head
7241 })
7242 } else {
7243 false
7244 };
7245
7246 if all_selections_inside_invalidation_ranges {
7247 break;
7248 } else {
7249 self.pop();
7250 }
7251 }
7252 }
7253}
7254
7255impl<T> Default for InvalidationStack<T> {
7256 fn default() -> Self {
7257 Self(Default::default())
7258 }
7259}
7260
7261impl<T> Deref for InvalidationStack<T> {
7262 type Target = Vec<T>;
7263
7264 fn deref(&self) -> &Self::Target {
7265 &self.0
7266 }
7267}
7268
7269impl<T> DerefMut for InvalidationStack<T> {
7270 fn deref_mut(&mut self) -> &mut Self::Target {
7271 &mut self.0
7272 }
7273}
7274
7275impl InvalidationRegion for SnippetState {
7276 fn ranges(&self) -> &[Range<Anchor>] {
7277 &self.ranges[self.active_index]
7278 }
7279}
7280
7281impl Deref for EditorStyle {
7282 type Target = theme::Editor;
7283
7284 fn deref(&self) -> &Self::Target {
7285 &self.theme
7286 }
7287}
7288
7289pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
7290 let mut highlighted_lines = Vec::new();
7291 for line in diagnostic.message.lines() {
7292 highlighted_lines.push(highlight_diagnostic_message(line));
7293 }
7294
7295 Arc::new(move |cx: &mut BlockContext| {
7296 let settings = cx.global::<Settings>();
7297 let theme = &settings.theme.editor;
7298 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
7299 let font_size = (style.text_scale_factor * settings.buffer_font_size).round();
7300 Flex::column()
7301 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
7302 Label::new(
7303 line.clone(),
7304 style.message.clone().with_font_size(font_size),
7305 )
7306 .with_highlights(highlights.clone())
7307 .contained()
7308 .with_margin_left(cx.anchor_x)
7309 .boxed()
7310 }))
7311 .aligned()
7312 .left()
7313 .boxed()
7314 })
7315}
7316
7317pub fn highlight_diagnostic_message(message: &str) -> (String, Vec<usize>) {
7318 let mut message_without_backticks = String::new();
7319 let mut prev_offset = 0;
7320 let mut inside_block = false;
7321 let mut highlights = Vec::new();
7322 for (match_ix, (offset, _)) in message
7323 .match_indices('`')
7324 .chain([(message.len(), "")])
7325 .enumerate()
7326 {
7327 message_without_backticks.push_str(&message[prev_offset..offset]);
7328 if inside_block {
7329 highlights.extend(prev_offset - match_ix..offset - match_ix);
7330 }
7331
7332 inside_block = !inside_block;
7333 prev_offset = offset + 1;
7334 }
7335
7336 (message_without_backticks, highlights)
7337}
7338
7339pub fn diagnostic_style(
7340 severity: DiagnosticSeverity,
7341 valid: bool,
7342 theme: &theme::Editor,
7343) -> DiagnosticStyle {
7344 match (severity, valid) {
7345 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
7346 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
7347 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
7348 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
7349 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
7350 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
7351 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
7352 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
7353 _ => theme.invalid_hint_diagnostic.clone(),
7354 }
7355}
7356
7357pub fn combine_syntax_and_fuzzy_match_highlights(
7358 text: &str,
7359 default_style: HighlightStyle,
7360 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
7361 match_indices: &[usize],
7362) -> Vec<(Range<usize>, HighlightStyle)> {
7363 let mut result = Vec::new();
7364 let mut match_indices = match_indices.iter().copied().peekable();
7365
7366 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
7367 {
7368 syntax_highlight.weight = None;
7369
7370 // Add highlights for any fuzzy match characters before the next
7371 // syntax highlight range.
7372 while let Some(&match_index) = match_indices.peek() {
7373 if match_index >= range.start {
7374 break;
7375 }
7376 match_indices.next();
7377 let end_index = char_ix_after(match_index, text);
7378 let mut match_style = default_style;
7379 match_style.weight = Some(fonts::Weight::BOLD);
7380 result.push((match_index..end_index, match_style));
7381 }
7382
7383 if range.start == usize::MAX {
7384 break;
7385 }
7386
7387 // Add highlights for any fuzzy match characters within the
7388 // syntax highlight range.
7389 let mut offset = range.start;
7390 while let Some(&match_index) = match_indices.peek() {
7391 if match_index >= range.end {
7392 break;
7393 }
7394
7395 match_indices.next();
7396 if match_index > offset {
7397 result.push((offset..match_index, syntax_highlight));
7398 }
7399
7400 let mut end_index = char_ix_after(match_index, text);
7401 while let Some(&next_match_index) = match_indices.peek() {
7402 if next_match_index == end_index && next_match_index < range.end {
7403 end_index = char_ix_after(next_match_index, text);
7404 match_indices.next();
7405 } else {
7406 break;
7407 }
7408 }
7409
7410 let mut match_style = syntax_highlight;
7411 match_style.weight = Some(fonts::Weight::BOLD);
7412 result.push((match_index..end_index, match_style));
7413 offset = end_index;
7414 }
7415
7416 if offset < range.end {
7417 result.push((offset..range.end, syntax_highlight));
7418 }
7419 }
7420
7421 fn char_ix_after(ix: usize, text: &str) -> usize {
7422 ix + text[ix..].chars().next().unwrap().len_utf8()
7423 }
7424
7425 result
7426}
7427
7428pub fn styled_runs_for_code_label<'a>(
7429 label: &'a CodeLabel,
7430 syntax_theme: &'a theme::SyntaxTheme,
7431) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
7432 let fade_out = HighlightStyle {
7433 fade_out: Some(0.35),
7434 ..Default::default()
7435 };
7436
7437 let mut prev_end = label.filter_range.end;
7438 label
7439 .runs
7440 .iter()
7441 .enumerate()
7442 .flat_map(move |(ix, (range, highlight_id))| {
7443 let style = if let Some(style) = highlight_id.style(syntax_theme) {
7444 style
7445 } else {
7446 return Default::default();
7447 };
7448 let mut muted_style = style;
7449 muted_style.highlight(fade_out);
7450
7451 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
7452 if range.start >= label.filter_range.end {
7453 if range.start > prev_end {
7454 runs.push((prev_end..range.start, fade_out));
7455 }
7456 runs.push((range.clone(), muted_style));
7457 } else if range.end <= label.filter_range.end {
7458 runs.push((range.clone(), style));
7459 } else {
7460 runs.push((range.start..label.filter_range.end, style));
7461 runs.push((label.filter_range.end..range.end, muted_style));
7462 }
7463 prev_end = cmp::max(prev_end, range.end);
7464
7465 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
7466 runs.push((prev_end..label.text.len(), fade_out));
7467 }
7468
7469 runs
7470 })
7471}
7472
7473pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
7474 let mut index = 0;
7475 let mut codepoints = text.char_indices().peekable();
7476
7477 std::iter::from_fn(move || {
7478 let start_index = index;
7479 while let Some((new_index, codepoint)) = codepoints.next() {
7480 index = new_index + codepoint.len_utf8();
7481 let current_upper = codepoint.is_uppercase();
7482 let next_upper = codepoints
7483 .peek()
7484 .map(|(_, c)| c.is_uppercase())
7485 .unwrap_or(false);
7486
7487 if !current_upper && next_upper {
7488 return Some(&text[start_index..index]);
7489 }
7490 }
7491
7492 index = text.len();
7493 if start_index < text.len() {
7494 return Some(&text[start_index..]);
7495 }
7496 None
7497 })
7498 .flat_map(|word| word.split_inclusive('_'))
7499}
7500
7501trait RangeToAnchorExt {
7502 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
7503}
7504
7505impl<T: ToOffset> RangeToAnchorExt for Range<T> {
7506 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
7507 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
7508 }
7509}