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