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