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