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