1pub mod display_map;
2mod element;
3mod highlight_matching_bracket;
4mod hover_popover;
5pub mod items;
6mod link_go_to_definition;
7pub mod movement;
8mod multi_buffer;
9pub mod selections_collection;
10
11#[cfg(any(test, feature = "test-support"))]
12pub mod test;
13
14use aho_corasick::AhoCorasick;
15use anyhow::Result;
16use clock::ReplicaId;
17use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
18pub use display_map::DisplayPoint;
19use display_map::*;
20pub use element::*;
21use fuzzy::{StringMatch, StringMatchCandidate};
22use gpui::{
23 actions,
24 color::Color,
25 elements::*,
26 executor,
27 fonts::{self, HighlightStyle, TextStyle},
28 geometry::vector::{vec2f, Vector2F},
29 impl_actions, impl_internal_actions,
30 platform::CursorStyle,
31 text_layout, AppContext, AsyncAppContext, ClipboardItem, Element, ElementBox, Entity,
32 ModelHandle, MutableAppContext, RenderContext, Subscription, Task, View, ViewContext,
33 ViewHandle, WeakViewHandle,
34};
35use highlight_matching_bracket::refresh_matching_bracket_highlights;
36use hover_popover::{hide_hover, HoverState};
37pub use language::{char_kind, CharKind};
38use language::{
39 BracketPair, Buffer, CodeAction, CodeLabel, Completion, Diagnostic, DiagnosticSeverity,
40 IndentKind, IndentSize, Language, OffsetRangeExt, Point, Selection, SelectionGoal,
41 TransactionId,
42};
43use link_go_to_definition::LinkGoToDefinitionState;
44use multi_buffer::MultiBufferChunks;
45pub use multi_buffer::{
46 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
47 ToPoint,
48};
49use ordered_float::OrderedFloat;
50use project::{LocationLink, Project, ProjectPath, ProjectTransaction};
51use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection};
52use serde::{Deserialize, Serialize};
53use settings::Settings;
54use smallvec::SmallVec;
55use smol::Timer;
56use snippet::Snippet;
57use std::{
58 any::TypeId,
59 borrow::Cow,
60 cmp::{self, Ordering, Reverse},
61 mem,
62 num::NonZeroU32,
63 ops::{Deref, DerefMut, Range, RangeInclusive},
64 sync::Arc,
65 time::{Duration, Instant},
66};
67pub use sum_tree::Bias;
68use theme::{DiagnosticStyle, Theme};
69use util::{post_inc, ResultExt, TryFutureExt};
70use workspace::{ItemNavHistory, Workspace};
71
72const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
73const MAX_LINE_LEN: usize = 1024;
74const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
75const MAX_SELECTION_HISTORY_LEN: usize = 1024;
76
77#[derive(Clone, Deserialize, PartialEq)]
78pub struct SelectNext {
79 #[serde(default)]
80 pub replace_newest: bool,
81}
82
83#[derive(Clone, PartialEq)]
84pub struct GoToDiagnostic(pub Direction);
85
86#[derive(Clone, PartialEq)]
87pub struct Scroll(pub Vector2F);
88
89#[derive(Clone, PartialEq)]
90pub struct Select(pub SelectPhase);
91
92#[derive(Clone, Debug, PartialEq)]
93pub struct Jump {
94 path: ProjectPath,
95 position: Point,
96 anchor: language::Anchor,
97}
98
99#[derive(Clone, Deserialize, PartialEq)]
100pub struct Input(pub String);
101
102#[derive(Clone, Deserialize, PartialEq)]
103pub struct SelectToBeginningOfLine {
104 #[serde(default)]
105 stop_at_soft_wraps: bool,
106}
107
108#[derive(Clone, Deserialize, PartialEq)]
109pub struct SelectToEndOfLine {
110 #[serde(default)]
111 stop_at_soft_wraps: bool,
112}
113
114#[derive(Clone, Deserialize, PartialEq)]
115pub struct ToggleCodeActions {
116 #[serde(default)]
117 pub deployed_from_indicator: bool,
118}
119
120#[derive(Clone, Default, Deserialize, PartialEq)]
121pub struct ConfirmCompletion {
122 #[serde(default)]
123 pub item_ix: Option<usize>,
124}
125
126#[derive(Clone, Default, Deserialize, PartialEq)]
127pub struct ConfirmCodeAction {
128 #[serde(default)]
129 pub item_ix: Option<usize>,
130}
131
132actions!(
133 editor,
134 [
135 Cancel,
136 Backspace,
137 Delete,
138 Newline,
139 GoToNextDiagnostic,
140 GoToPrevDiagnostic,
141 Indent,
142 Outdent,
143 DeleteLine,
144 DeleteToPreviousWordStart,
145 DeleteToPreviousSubwordStart,
146 DeleteToNextWordEnd,
147 DeleteToNextSubwordEnd,
148 DeleteToBeginningOfLine,
149 DeleteToEndOfLine,
150 CutToEndOfLine,
151 DuplicateLine,
152 MoveLineUp,
153 MoveLineDown,
154 Transpose,
155 Cut,
156 Copy,
157 Paste,
158 Undo,
159 Redo,
160 MoveUp,
161 MoveDown,
162 MoveLeft,
163 MoveRight,
164 MoveToPreviousWordStart,
165 MoveToPreviousSubwordStart,
166 MoveToNextWordEnd,
167 MoveToNextSubwordEnd,
168 MoveToBeginningOfLine,
169 MoveToEndOfLine,
170 MoveToBeginning,
171 MoveToEnd,
172 SelectUp,
173 SelectDown,
174 SelectLeft,
175 SelectRight,
176 SelectToPreviousWordStart,
177 SelectToPreviousSubwordStart,
178 SelectToNextWordEnd,
179 SelectToNextSubwordEnd,
180 SelectToBeginning,
181 SelectToEnd,
182 SelectAll,
183 SelectLine,
184 SplitSelectionIntoLines,
185 AddSelectionAbove,
186 AddSelectionBelow,
187 Tab,
188 TabPrev,
189 ToggleComments,
190 SelectLargerSyntaxNode,
191 SelectSmallerSyntaxNode,
192 GoToDefinition,
193 MoveToEnclosingBracket,
194 UndoSelection,
195 RedoSelection,
196 FindAllReferences,
197 Rename,
198 ConfirmRename,
199 PageUp,
200 PageDown,
201 Fold,
202 UnfoldLines,
203 FoldSelectedRanges,
204 ShowCompletions,
205 OpenExcerpts,
206 RestartLanguageServer,
207 Hover,
208 ]
209);
210
211impl_actions!(
212 editor,
213 [
214 Input,
215 SelectNext,
216 SelectToBeginningOfLine,
217 SelectToEndOfLine,
218 ToggleCodeActions,
219 ConfirmCompletion,
220 ConfirmCodeAction,
221 ]
222);
223
224impl_internal_actions!(editor, [Scroll, Select, Jump]);
225
226enum DocumentHighlightRead {}
227enum DocumentHighlightWrite {}
228
229#[derive(Copy, Clone, PartialEq, Eq)]
230pub enum Direction {
231 Prev,
232 Next,
233}
234
235pub fn init(cx: &mut MutableAppContext) {
236 cx.add_action(Editor::new_file);
237 cx.add_action(|this: &mut Editor, action: &Scroll, cx| this.set_scroll_position(action.0, cx));
238 cx.add_action(Editor::select);
239 cx.add_action(Editor::cancel);
240 cx.add_action(Editor::handle_input);
241 cx.add_action(Editor::newline);
242 cx.add_action(Editor::backspace);
243 cx.add_action(Editor::delete);
244 cx.add_action(Editor::tab);
245 cx.add_action(Editor::tab_prev);
246 cx.add_action(Editor::indent);
247 cx.add_action(Editor::outdent);
248 cx.add_action(Editor::delete_line);
249 cx.add_action(Editor::delete_to_previous_word_start);
250 cx.add_action(Editor::delete_to_previous_subword_start);
251 cx.add_action(Editor::delete_to_next_word_end);
252 cx.add_action(Editor::delete_to_next_subword_end);
253 cx.add_action(Editor::delete_to_beginning_of_line);
254 cx.add_action(Editor::delete_to_end_of_line);
255 cx.add_action(Editor::cut_to_end_of_line);
256 cx.add_action(Editor::duplicate_line);
257 cx.add_action(Editor::move_line_up);
258 cx.add_action(Editor::move_line_down);
259 cx.add_action(Editor::transpose);
260 cx.add_action(Editor::cut);
261 cx.add_action(Editor::copy);
262 cx.add_action(Editor::paste);
263 cx.add_action(Editor::undo);
264 cx.add_action(Editor::redo);
265 cx.add_action(Editor::move_up);
266 cx.add_action(Editor::move_down);
267 cx.add_action(Editor::move_left);
268 cx.add_action(Editor::move_right);
269 cx.add_action(Editor::move_to_previous_word_start);
270 cx.add_action(Editor::move_to_previous_subword_start);
271 cx.add_action(Editor::move_to_next_word_end);
272 cx.add_action(Editor::move_to_next_subword_end);
273 cx.add_action(Editor::move_to_beginning_of_line);
274 cx.add_action(Editor::move_to_end_of_line);
275 cx.add_action(Editor::move_to_beginning);
276 cx.add_action(Editor::move_to_end);
277 cx.add_action(Editor::select_up);
278 cx.add_action(Editor::select_down);
279 cx.add_action(Editor::select_left);
280 cx.add_action(Editor::select_right);
281 cx.add_action(Editor::select_to_previous_word_start);
282 cx.add_action(Editor::select_to_previous_subword_start);
283 cx.add_action(Editor::select_to_next_word_end);
284 cx.add_action(Editor::select_to_next_subword_end);
285 cx.add_action(Editor::select_to_beginning_of_line);
286 cx.add_action(Editor::select_to_end_of_line);
287 cx.add_action(Editor::select_to_beginning);
288 cx.add_action(Editor::select_to_end);
289 cx.add_action(Editor::select_all);
290 cx.add_action(Editor::select_line);
291 cx.add_action(Editor::split_selection_into_lines);
292 cx.add_action(Editor::add_selection_above);
293 cx.add_action(Editor::add_selection_below);
294 cx.add_action(Editor::select_next);
295 cx.add_action(Editor::toggle_comments);
296 cx.add_action(Editor::select_larger_syntax_node);
297 cx.add_action(Editor::select_smaller_syntax_node);
298 cx.add_action(Editor::move_to_enclosing_bracket);
299 cx.add_action(Editor::undo_selection);
300 cx.add_action(Editor::redo_selection);
301 cx.add_action(Editor::go_to_next_diagnostic);
302 cx.add_action(Editor::go_to_prev_diagnostic);
303 cx.add_action(Editor::go_to_definition);
304 cx.add_action(Editor::page_up);
305 cx.add_action(Editor::page_down);
306 cx.add_action(Editor::fold);
307 cx.add_action(Editor::unfold_lines);
308 cx.add_action(Editor::fold_selected_ranges);
309 cx.add_action(Editor::show_completions);
310 cx.add_action(Editor::toggle_code_actions);
311 cx.add_action(Editor::open_excerpts);
312 cx.add_action(Editor::jump);
313 cx.add_action(Editor::restart_language_server);
314 cx.add_async_action(Editor::confirm_completion);
315 cx.add_async_action(Editor::confirm_code_action);
316 cx.add_async_action(Editor::rename);
317 cx.add_async_action(Editor::confirm_rename);
318 cx.add_async_action(Editor::find_all_references);
319
320 hover_popover::init(cx);
321 link_go_to_definition::init(cx);
322
323 workspace::register_project_item::<Editor>(cx);
324 workspace::register_followable_item::<Editor>(cx);
325}
326
327trait InvalidationRegion {
328 fn ranges(&self) -> &[Range<Anchor>];
329}
330
331#[derive(Clone, Debug, PartialEq)]
332pub enum SelectPhase {
333 Begin {
334 position: DisplayPoint,
335 add: bool,
336 click_count: usize,
337 },
338 BeginColumnar {
339 position: DisplayPoint,
340 overshoot: u32,
341 },
342 Extend {
343 position: DisplayPoint,
344 click_count: usize,
345 },
346 Update {
347 position: DisplayPoint,
348 overshoot: u32,
349 scroll_position: Vector2F,
350 },
351 End,
352}
353
354#[derive(Clone, Debug)]
355pub enum SelectMode {
356 Character,
357 Word(Range<Anchor>),
358 Line(Range<Anchor>),
359 All,
360}
361
362#[derive(PartialEq, Eq)]
363pub enum Autoscroll {
364 Fit,
365 Center,
366 Newest,
367}
368
369#[derive(Copy, Clone, PartialEq, Eq)]
370pub enum EditorMode {
371 SingleLine,
372 AutoHeight { max_lines: usize },
373 Full,
374}
375
376#[derive(Clone)]
377pub enum SoftWrap {
378 None,
379 EditorWidth,
380 Column(u32),
381}
382
383#[derive(Clone)]
384pub struct EditorStyle {
385 pub text: TextStyle,
386 pub placeholder_text: Option<TextStyle>,
387 pub theme: theme::Editor,
388}
389
390type CompletionId = usize;
391
392pub type GetFieldEditorTheme = fn(&theme::Theme) -> theme::FieldEditor;
393
394type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
395
396pub struct Editor {
397 handle: WeakViewHandle<Self>,
398 buffer: ModelHandle<MultiBuffer>,
399 display_map: ModelHandle<DisplayMap>,
400 pub selections: SelectionsCollection,
401 columnar_selection_tail: Option<Anchor>,
402 add_selections_state: Option<AddSelectionsState>,
403 select_next_state: Option<SelectNextState>,
404 selection_history: SelectionHistory,
405 autoclose_stack: InvalidationStack<BracketPairState>,
406 snippet_stack: InvalidationStack<SnippetState>,
407 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
408 active_diagnostics: Option<ActiveDiagnosticGroup>,
409 scroll_position: Vector2F,
410 scroll_top_anchor: Anchor,
411 autoscroll_request: Option<(Autoscroll, bool)>,
412 soft_wrap_mode_override: Option<settings::SoftWrap>,
413 get_field_editor_theme: Option<GetFieldEditorTheme>,
414 override_text_style: Option<Box<OverrideTextStyle>>,
415 project: Option<ModelHandle<Project>>,
416 focused: bool,
417 show_local_cursors: bool,
418 show_local_selections: bool,
419 blink_epoch: usize,
420 blinking_paused: bool,
421 mode: EditorMode,
422 vertical_scroll_margin: f32,
423 placeholder_text: Option<Arc<str>>,
424 highlighted_rows: Option<Range<u32>>,
425 background_highlights: BTreeMap<TypeId, (fn(&Theme) -> Color, Vec<Range<Anchor>>)>,
426 nav_history: Option<ItemNavHistory>,
427 context_menu: Option<ContextMenu>,
428 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
429 next_completion_id: CompletionId,
430 available_code_actions: Option<(ModelHandle<Buffer>, Arc<[CodeAction]>)>,
431 code_actions_task: Option<Task<()>>,
432 document_highlights_task: Option<Task<()>>,
433 pending_rename: Option<RenameState>,
434 searchable: bool,
435 cursor_shape: CursorShape,
436 keymap_context_layers: BTreeMap<TypeId, gpui::keymap::Context>,
437 input_enabled: bool,
438 leader_replica_id: Option<u16>,
439 hover_state: HoverState,
440 link_go_to_definition_state: LinkGoToDefinitionState,
441 _subscriptions: Vec<Subscription>,
442}
443
444pub struct EditorSnapshot {
445 pub mode: EditorMode,
446 pub display_snapshot: DisplaySnapshot,
447 pub placeholder_text: Option<Arc<str>>,
448 is_focused: bool,
449 scroll_position: Vector2F,
450 scroll_top_anchor: Anchor,
451}
452
453#[derive(Clone, Debug)]
454struct SelectionHistoryEntry {
455 selections: Arc<[Selection<Anchor>]>,
456 select_next_state: Option<SelectNextState>,
457 add_selections_state: Option<AddSelectionsState>,
458}
459
460enum SelectionHistoryMode {
461 Normal,
462 Undoing,
463 Redoing,
464}
465
466impl Default for SelectionHistoryMode {
467 fn default() -> Self {
468 Self::Normal
469 }
470}
471
472#[derive(Default)]
473struct SelectionHistory {
474 selections_by_transaction:
475 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
476 mode: SelectionHistoryMode,
477 undo_stack: VecDeque<SelectionHistoryEntry>,
478 redo_stack: VecDeque<SelectionHistoryEntry>,
479}
480
481impl SelectionHistory {
482 fn insert_transaction(
483 &mut self,
484 transaction_id: TransactionId,
485 selections: Arc<[Selection<Anchor>]>,
486 ) {
487 self.selections_by_transaction
488 .insert(transaction_id, (selections, None));
489 }
490
491 fn transaction(
492 &self,
493 transaction_id: TransactionId,
494 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
495 self.selections_by_transaction.get(&transaction_id)
496 }
497
498 fn transaction_mut(
499 &mut self,
500 transaction_id: TransactionId,
501 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
502 self.selections_by_transaction.get_mut(&transaction_id)
503 }
504
505 fn push(&mut self, entry: SelectionHistoryEntry) {
506 if !entry.selections.is_empty() {
507 match self.mode {
508 SelectionHistoryMode::Normal => {
509 self.push_undo(entry);
510 self.redo_stack.clear();
511 }
512 SelectionHistoryMode::Undoing => self.push_redo(entry),
513 SelectionHistoryMode::Redoing => self.push_undo(entry),
514 }
515 }
516 }
517
518 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
519 if self
520 .undo_stack
521 .back()
522 .map_or(true, |e| e.selections != entry.selections)
523 {
524 self.undo_stack.push_back(entry);
525 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
526 self.undo_stack.pop_front();
527 }
528 }
529 }
530
531 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
532 if self
533 .redo_stack
534 .back()
535 .map_or(true, |e| e.selections != entry.selections)
536 {
537 self.redo_stack.push_back(entry);
538 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
539 self.redo_stack.pop_front();
540 }
541 }
542 }
543}
544
545#[derive(Clone, Debug)]
546struct AddSelectionsState {
547 above: bool,
548 stack: Vec<usize>,
549}
550
551#[derive(Clone, Debug)]
552struct SelectNextState {
553 query: AhoCorasick,
554 wordwise: bool,
555 done: bool,
556}
557
558struct BracketPairState {
559 ranges: Vec<Range<Anchor>>,
560 pair: BracketPair,
561}
562
563#[derive(Debug)]
564struct SnippetState {
565 ranges: Vec<Vec<Range<Anchor>>>,
566 active_index: usize,
567}
568
569pub struct RenameState {
570 pub range: Range<Anchor>,
571 pub old_name: Arc<str>,
572 pub editor: ViewHandle<Editor>,
573 block_id: BlockId,
574}
575
576struct InvalidationStack<T>(Vec<T>);
577
578enum ContextMenu {
579 Completions(CompletionsMenu),
580 CodeActions(CodeActionsMenu),
581}
582
583impl ContextMenu {
584 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) -> bool {
585 if self.visible() {
586 match self {
587 ContextMenu::Completions(menu) => menu.select_prev(cx),
588 ContextMenu::CodeActions(menu) => menu.select_prev(cx),
589 }
590 true
591 } else {
592 false
593 }
594 }
595
596 fn select_next(&mut self, cx: &mut ViewContext<Editor>) -> bool {
597 if self.visible() {
598 match self {
599 ContextMenu::Completions(menu) => menu.select_next(cx),
600 ContextMenu::CodeActions(menu) => menu.select_next(cx),
601 }
602 true
603 } else {
604 false
605 }
606 }
607
608 fn visible(&self) -> bool {
609 match self {
610 ContextMenu::Completions(menu) => menu.visible(),
611 ContextMenu::CodeActions(menu) => menu.visible(),
612 }
613 }
614
615 fn render(
616 &self,
617 cursor_position: DisplayPoint,
618 style: EditorStyle,
619 cx: &mut RenderContext<Editor>,
620 ) -> (DisplayPoint, ElementBox) {
621 match self {
622 ContextMenu::Completions(menu) => (cursor_position, menu.render(style, cx)),
623 ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, cx),
624 }
625 }
626}
627
628struct CompletionsMenu {
629 id: CompletionId,
630 initial_position: Anchor,
631 buffer: ModelHandle<Buffer>,
632 completions: Arc<[Completion]>,
633 match_candidates: Vec<StringMatchCandidate>,
634 matches: Arc<[StringMatch]>,
635 selected_item: usize,
636 list: UniformListState,
637}
638
639impl CompletionsMenu {
640 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
641 if self.selected_item > 0 {
642 self.selected_item -= 1;
643 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
644 }
645 cx.notify();
646 }
647
648 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
649 if self.selected_item + 1 < self.matches.len() {
650 self.selected_item += 1;
651 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
652 }
653 cx.notify();
654 }
655
656 fn visible(&self) -> bool {
657 !self.matches.is_empty()
658 }
659
660 fn render(&self, style: EditorStyle, cx: &mut RenderContext<Editor>) -> ElementBox {
661 enum CompletionTag {}
662
663 let completions = self.completions.clone();
664 let matches = self.matches.clone();
665 let selected_item = self.selected_item;
666 let container_style = style.autocomplete.container;
667 UniformList::new(
668 self.list.clone(),
669 matches.len(),
670 cx,
671 move |_, range, items, cx| {
672 let start_ix = range.start;
673 for (ix, mat) in matches[range].iter().enumerate() {
674 let completion = &completions[mat.candidate_id];
675 let item_ix = start_ix + ix;
676 items.push(
677 MouseEventHandler::new::<CompletionTag, _, _>(
678 mat.candidate_id,
679 cx,
680 |state, _| {
681 let item_style = if item_ix == selected_item {
682 style.autocomplete.selected_item
683 } else if state.hovered {
684 style.autocomplete.hovered_item
685 } else {
686 style.autocomplete.item
687 };
688
689 Text::new(completion.label.text.clone(), style.text.clone())
690 .with_soft_wrap(false)
691 .with_highlights(combine_syntax_and_fuzzy_match_highlights(
692 &completion.label.text,
693 style.text.color.into(),
694 styled_runs_for_code_label(
695 &completion.label,
696 &style.syntax,
697 ),
698 &mat.positions,
699 ))
700 .contained()
701 .with_style(item_style)
702 .boxed()
703 },
704 )
705 .with_cursor_style(CursorStyle::PointingHand)
706 .on_mouse_down(move |_, cx| {
707 cx.dispatch_action(ConfirmCompletion {
708 item_ix: Some(item_ix),
709 });
710 })
711 .boxed(),
712 );
713 }
714 },
715 )
716 .with_width_from_item(
717 self.matches
718 .iter()
719 .enumerate()
720 .max_by_key(|(_, mat)| {
721 self.completions[mat.candidate_id]
722 .label
723 .text
724 .chars()
725 .count()
726 })
727 .map(|(ix, _)| ix),
728 )
729 .contained()
730 .with_style(container_style)
731 .boxed()
732 }
733
734 pub async fn filter(&mut self, query: Option<&str>, executor: Arc<executor::Background>) {
735 let mut matches = if let Some(query) = query {
736 fuzzy::match_strings(
737 &self.match_candidates,
738 query,
739 false,
740 100,
741 &Default::default(),
742 executor,
743 )
744 .await
745 } else {
746 self.match_candidates
747 .iter()
748 .enumerate()
749 .map(|(candidate_id, candidate)| StringMatch {
750 candidate_id,
751 score: Default::default(),
752 positions: Default::default(),
753 string: candidate.string.clone(),
754 })
755 .collect()
756 };
757 matches.sort_unstable_by_key(|mat| {
758 (
759 Reverse(OrderedFloat(mat.score)),
760 self.completions[mat.candidate_id].sort_key(),
761 )
762 });
763
764 for mat in &mut matches {
765 let filter_start = self.completions[mat.candidate_id].label.filter_range.start;
766 for position in &mut mat.positions {
767 *position += filter_start;
768 }
769 }
770
771 self.matches = matches.into();
772 }
773}
774
775#[derive(Clone)]
776struct CodeActionsMenu {
777 actions: Arc<[CodeAction]>,
778 buffer: ModelHandle<Buffer>,
779 selected_item: usize,
780 list: UniformListState,
781 deployed_from_indicator: bool,
782}
783
784impl CodeActionsMenu {
785 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
786 if self.selected_item > 0 {
787 self.selected_item -= 1;
788 cx.notify()
789 }
790 }
791
792 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
793 if self.selected_item + 1 < self.actions.len() {
794 self.selected_item += 1;
795 cx.notify()
796 }
797 }
798
799 fn visible(&self) -> bool {
800 !self.actions.is_empty()
801 }
802
803 fn render(
804 &self,
805 mut cursor_position: DisplayPoint,
806 style: EditorStyle,
807 cx: &mut RenderContext<Editor>,
808 ) -> (DisplayPoint, ElementBox) {
809 enum ActionTag {}
810
811 let container_style = style.autocomplete.container;
812 let actions = self.actions.clone();
813 let selected_item = self.selected_item;
814 let element = UniformList::new(
815 self.list.clone(),
816 actions.len(),
817 cx,
818 move |_, range, items, cx| {
819 let start_ix = range.start;
820 for (ix, action) in actions[range].iter().enumerate() {
821 let item_ix = start_ix + ix;
822 items.push(
823 MouseEventHandler::new::<ActionTag, _, _>(item_ix, cx, |state, _| {
824 let item_style = if item_ix == selected_item {
825 style.autocomplete.selected_item
826 } else if state.hovered {
827 style.autocomplete.hovered_item
828 } else {
829 style.autocomplete.item
830 };
831
832 Text::new(action.lsp_action.title.clone(), style.text.clone())
833 .with_soft_wrap(false)
834 .contained()
835 .with_style(item_style)
836 .boxed()
837 })
838 .with_cursor_style(CursorStyle::PointingHand)
839 .on_mouse_down(move |_, cx| {
840 cx.dispatch_action(ConfirmCodeAction {
841 item_ix: Some(item_ix),
842 });
843 })
844 .boxed(),
845 );
846 }
847 },
848 )
849 .with_width_from_item(
850 self.actions
851 .iter()
852 .enumerate()
853 .max_by_key(|(_, action)| action.lsp_action.title.chars().count())
854 .map(|(ix, _)| ix),
855 )
856 .contained()
857 .with_style(container_style)
858 .boxed();
859
860 if self.deployed_from_indicator {
861 *cursor_position.column_mut() = 0;
862 }
863
864 (cursor_position, element)
865 }
866}
867
868#[derive(Debug)]
869struct ActiveDiagnosticGroup {
870 primary_range: Range<Anchor>,
871 primary_message: String,
872 blocks: HashMap<BlockId, Diagnostic>,
873 is_valid: bool,
874}
875
876#[derive(Serialize, Deserialize)]
877pub struct ClipboardSelection {
878 pub len: usize,
879 pub is_entire_line: bool,
880}
881
882#[derive(Debug)]
883pub struct NavigationData {
884 // Matching offsets for anchor and scroll_top_anchor allows us to recreate the anchor if the buffer
885 // has since been closed
886 cursor_anchor: Anchor,
887 cursor_position: Point,
888 scroll_position: Vector2F,
889 scroll_top_anchor: Anchor,
890 scroll_top_row: u32,
891}
892
893pub struct EditorCreated(pub ViewHandle<Editor>);
894
895impl Editor {
896 pub fn single_line(
897 field_editor_style: Option<GetFieldEditorTheme>,
898 cx: &mut ViewContext<Self>,
899 ) -> Self {
900 let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx));
901 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
902 Self::new(EditorMode::SingleLine, buffer, None, field_editor_style, cx)
903 }
904
905 pub fn auto_height(
906 max_lines: usize,
907 field_editor_style: Option<GetFieldEditorTheme>,
908 cx: &mut ViewContext<Self>,
909 ) -> Self {
910 let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx));
911 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
912 Self::new(
913 EditorMode::AutoHeight { max_lines },
914 buffer,
915 None,
916 field_editor_style,
917 cx,
918 )
919 }
920
921 pub fn for_buffer(
922 buffer: ModelHandle<Buffer>,
923 project: Option<ModelHandle<Project>>,
924 cx: &mut ViewContext<Self>,
925 ) -> Self {
926 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
927 Self::new(EditorMode::Full, buffer, project, None, cx)
928 }
929
930 pub fn for_multibuffer(
931 buffer: ModelHandle<MultiBuffer>,
932 project: Option<ModelHandle<Project>>,
933 cx: &mut ViewContext<Self>,
934 ) -> Self {
935 Self::new(EditorMode::Full, buffer, project, None, cx)
936 }
937
938 pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
939 let mut clone = Self::new(
940 self.mode,
941 self.buffer.clone(),
942 self.project.clone(),
943 self.get_field_editor_theme,
944 cx,
945 );
946 self.display_map.update(cx, |display_map, cx| {
947 let snapshot = display_map.snapshot(cx);
948 clone.display_map.update(cx, |display_map, cx| {
949 display_map.set_state(&snapshot, cx);
950 });
951 });
952 clone.selections.set_state(&self.selections);
953 clone.scroll_position = self.scroll_position;
954 clone.scroll_top_anchor = self.scroll_top_anchor.clone();
955 clone.searchable = self.searchable;
956 clone
957 }
958
959 fn new(
960 mode: EditorMode,
961 buffer: ModelHandle<MultiBuffer>,
962 project: Option<ModelHandle<Project>>,
963 get_field_editor_theme: Option<GetFieldEditorTheme>,
964 cx: &mut ViewContext<Self>,
965 ) -> Self {
966 let display_map = cx.add_model(|cx| {
967 let settings = cx.global::<Settings>();
968 let style = build_style(&*settings, get_field_editor_theme, None, cx);
969 DisplayMap::new(
970 buffer.clone(),
971 style.text.font_id,
972 style.text.font_size,
973 None,
974 2,
975 1,
976 cx,
977 )
978 });
979
980 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
981
982 let mut this = Self {
983 handle: cx.weak_handle(),
984 buffer: buffer.clone(),
985 display_map: display_map.clone(),
986 selections,
987 columnar_selection_tail: None,
988 add_selections_state: None,
989 select_next_state: None,
990 selection_history: Default::default(),
991 autoclose_stack: Default::default(),
992 snippet_stack: Default::default(),
993 select_larger_syntax_node_stack: Vec::new(),
994 active_diagnostics: None,
995 soft_wrap_mode_override: None,
996 get_field_editor_theme,
997 project,
998 scroll_position: Vector2F::zero(),
999 scroll_top_anchor: Anchor::min(),
1000 autoscroll_request: None,
1001 focused: false,
1002 show_local_cursors: false,
1003 show_local_selections: true,
1004 blink_epoch: 0,
1005 blinking_paused: false,
1006 mode,
1007 vertical_scroll_margin: 3.0,
1008 placeholder_text: None,
1009 highlighted_rows: None,
1010 background_highlights: Default::default(),
1011 nav_history: None,
1012 context_menu: None,
1013 completion_tasks: Default::default(),
1014 next_completion_id: 0,
1015 available_code_actions: Default::default(),
1016 code_actions_task: Default::default(),
1017
1018 document_highlights_task: Default::default(),
1019 pending_rename: Default::default(),
1020 searchable: true,
1021 override_text_style: None,
1022 cursor_shape: Default::default(),
1023 keymap_context_layers: Default::default(),
1024 input_enabled: true,
1025 leader_replica_id: None,
1026 hover_state: Default::default(),
1027 link_go_to_definition_state: Default::default(),
1028 _subscriptions: vec![
1029 cx.observe(&buffer, Self::on_buffer_changed),
1030 cx.subscribe(&buffer, Self::on_buffer_event),
1031 cx.observe(&display_map, Self::on_display_map_changed),
1032 ],
1033 };
1034 this.end_selection(cx);
1035
1036 let editor_created_event = EditorCreated(cx.handle());
1037 cx.emit_global(editor_created_event);
1038
1039 this
1040 }
1041
1042 pub fn new_file(
1043 workspace: &mut Workspace,
1044 _: &workspace::NewFile,
1045 cx: &mut ViewContext<Workspace>,
1046 ) {
1047 let project = workspace.project().clone();
1048 if project.read(cx).is_remote() {
1049 cx.propagate_action();
1050 } else if let Some(buffer) = project
1051 .update(cx, |project, cx| project.create_buffer("", None, cx))
1052 .log_err()
1053 {
1054 workspace.add_item(
1055 Box::new(cx.add_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx))),
1056 cx,
1057 );
1058 }
1059 }
1060
1061 pub fn replica_id(&self, cx: &AppContext) -> ReplicaId {
1062 self.buffer.read(cx).replica_id()
1063 }
1064
1065 pub fn leader_replica_id(&self) -> Option<ReplicaId> {
1066 self.leader_replica_id
1067 }
1068
1069 pub fn buffer(&self) -> &ModelHandle<MultiBuffer> {
1070 &self.buffer
1071 }
1072
1073 pub fn title(&self, cx: &AppContext) -> String {
1074 self.buffer().read(cx).title(cx)
1075 }
1076
1077 pub fn snapshot(&mut self, cx: &mut MutableAppContext) -> EditorSnapshot {
1078 EditorSnapshot {
1079 mode: self.mode,
1080 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1081 scroll_position: self.scroll_position,
1082 scroll_top_anchor: self.scroll_top_anchor.clone(),
1083 placeholder_text: self.placeholder_text.clone(),
1084 is_focused: self
1085 .handle
1086 .upgrade(cx)
1087 .map_or(false, |handle| handle.is_focused(cx)),
1088 }
1089 }
1090
1091 pub fn language_at<'a, T: ToOffset>(
1092 &self,
1093 point: T,
1094 cx: &'a AppContext,
1095 ) -> Option<&'a Arc<Language>> {
1096 self.buffer.read(cx).language_at(point, cx)
1097 }
1098
1099 fn style(&self, cx: &AppContext) -> EditorStyle {
1100 build_style(
1101 cx.global::<Settings>(),
1102 self.get_field_editor_theme,
1103 self.override_text_style.as_deref(),
1104 cx,
1105 )
1106 }
1107
1108 pub fn mode(&self) -> EditorMode {
1109 self.mode
1110 }
1111
1112 pub fn set_placeholder_text(
1113 &mut self,
1114 placeholder_text: impl Into<Arc<str>>,
1115 cx: &mut ViewContext<Self>,
1116 ) {
1117 self.placeholder_text = Some(placeholder_text.into());
1118 cx.notify();
1119 }
1120
1121 pub fn set_vertical_scroll_margin(&mut self, margin_rows: usize, cx: &mut ViewContext<Self>) {
1122 self.vertical_scroll_margin = margin_rows as f32;
1123 cx.notify();
1124 }
1125
1126 pub fn set_scroll_position(&mut self, scroll_position: Vector2F, cx: &mut ViewContext<Self>) {
1127 self.set_scroll_position_internal(scroll_position, true, cx);
1128 }
1129
1130 fn set_scroll_position_internal(
1131 &mut self,
1132 scroll_position: Vector2F,
1133 local: bool,
1134 cx: &mut ViewContext<Self>,
1135 ) {
1136 let map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1137
1138 if scroll_position.y() == 0. {
1139 self.scroll_top_anchor = Anchor::min();
1140 self.scroll_position = scroll_position;
1141 } else {
1142 let scroll_top_buffer_offset =
1143 DisplayPoint::new(scroll_position.y() as u32, 0).to_offset(&map, Bias::Right);
1144 let anchor = map
1145 .buffer_snapshot
1146 .anchor_at(scroll_top_buffer_offset, Bias::Right);
1147 self.scroll_position = vec2f(
1148 scroll_position.x(),
1149 scroll_position.y() - anchor.to_display_point(&map).row() as f32,
1150 );
1151 self.scroll_top_anchor = anchor;
1152 }
1153
1154 self.autoscroll_request.take();
1155 hide_hover(self, cx);
1156
1157 cx.emit(Event::ScrollPositionChanged { local });
1158 cx.notify();
1159 }
1160
1161 fn set_scroll_top_anchor(
1162 &mut self,
1163 anchor: Anchor,
1164 position: Vector2F,
1165 cx: &mut ViewContext<Self>,
1166 ) {
1167 self.scroll_top_anchor = anchor;
1168 self.scroll_position = position;
1169 cx.emit(Event::ScrollPositionChanged { local: false });
1170 cx.notify();
1171 }
1172
1173 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
1174 self.cursor_shape = cursor_shape;
1175 cx.notify();
1176 }
1177
1178 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut ViewContext<Self>) {
1179 if self.display_map.read(cx).clip_at_line_ends != clip {
1180 self.display_map
1181 .update(cx, |map, _| map.clip_at_line_ends = clip);
1182 }
1183 }
1184
1185 pub fn set_keymap_context_layer<Tag: 'static>(&mut self, context: gpui::keymap::Context) {
1186 self.keymap_context_layers
1187 .insert(TypeId::of::<Tag>(), context);
1188 }
1189
1190 pub fn remove_keymap_context_layer<Tag: 'static>(&mut self) {
1191 self.keymap_context_layers.remove(&TypeId::of::<Tag>());
1192 }
1193
1194 pub fn set_input_enabled(&mut self, input_enabled: bool) {
1195 self.input_enabled = input_enabled;
1196 }
1197
1198 pub fn scroll_position(&self, cx: &mut ViewContext<Self>) -> Vector2F {
1199 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1200 compute_scroll_position(&display_map, self.scroll_position, &self.scroll_top_anchor)
1201 }
1202
1203 pub fn clamp_scroll_left(&mut self, max: f32) -> bool {
1204 if max < self.scroll_position.x() {
1205 self.scroll_position.set_x(max);
1206 true
1207 } else {
1208 false
1209 }
1210 }
1211
1212 pub fn autoscroll_vertically(
1213 &mut self,
1214 viewport_height: f32,
1215 line_height: f32,
1216 cx: &mut ViewContext<Self>,
1217 ) -> bool {
1218 let visible_lines = viewport_height / line_height;
1219 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1220 let mut scroll_position =
1221 compute_scroll_position(&display_map, self.scroll_position, &self.scroll_top_anchor);
1222 let max_scroll_top = if matches!(self.mode, EditorMode::AutoHeight { .. }) {
1223 (display_map.max_point().row() as f32 - visible_lines + 1.).max(0.)
1224 } else {
1225 display_map.max_point().row().saturating_sub(1) as f32
1226 };
1227 if scroll_position.y() > max_scroll_top {
1228 scroll_position.set_y(max_scroll_top);
1229 self.set_scroll_position(scroll_position, cx);
1230 }
1231
1232 let (autoscroll, local) = if let Some(autoscroll) = self.autoscroll_request.take() {
1233 autoscroll
1234 } else {
1235 return false;
1236 };
1237
1238 let first_cursor_top;
1239 let last_cursor_bottom;
1240 if let Some(highlighted_rows) = &self.highlighted_rows {
1241 first_cursor_top = highlighted_rows.start as f32;
1242 last_cursor_bottom = first_cursor_top + 1.;
1243 } else if autoscroll == Autoscroll::Newest {
1244 let newest_selection = self.selections.newest::<Point>(cx);
1245 first_cursor_top = newest_selection.head().to_display_point(&display_map).row() as f32;
1246 last_cursor_bottom = first_cursor_top + 1.;
1247 } else {
1248 let selections = self.selections.all::<Point>(cx);
1249 first_cursor_top = selections
1250 .first()
1251 .unwrap()
1252 .head()
1253 .to_display_point(&display_map)
1254 .row() as f32;
1255 last_cursor_bottom = selections
1256 .last()
1257 .unwrap()
1258 .head()
1259 .to_display_point(&display_map)
1260 .row() as f32
1261 + 1.0;
1262 }
1263
1264 let margin = if matches!(self.mode, EditorMode::AutoHeight { .. }) {
1265 0.
1266 } else {
1267 ((visible_lines - (last_cursor_bottom - first_cursor_top)) / 2.0).floor()
1268 };
1269 if margin < 0.0 {
1270 return false;
1271 }
1272
1273 match autoscroll {
1274 Autoscroll::Fit | Autoscroll::Newest => {
1275 let margin = margin.min(self.vertical_scroll_margin);
1276 let target_top = (first_cursor_top - margin).max(0.0);
1277 let target_bottom = last_cursor_bottom + margin;
1278 let start_row = scroll_position.y();
1279 let end_row = start_row + visible_lines;
1280
1281 if target_top < start_row {
1282 scroll_position.set_y(target_top);
1283 self.set_scroll_position_internal(scroll_position, local, cx);
1284 } else if target_bottom >= end_row {
1285 scroll_position.set_y(target_bottom - visible_lines);
1286 self.set_scroll_position_internal(scroll_position, local, cx);
1287 }
1288 }
1289 Autoscroll::Center => {
1290 scroll_position.set_y((first_cursor_top - margin).max(0.0));
1291 self.set_scroll_position_internal(scroll_position, local, cx);
1292 }
1293 }
1294
1295 true
1296 }
1297
1298 pub fn autoscroll_horizontally(
1299 &mut self,
1300 start_row: u32,
1301 viewport_width: f32,
1302 scroll_width: f32,
1303 max_glyph_width: f32,
1304 layouts: &[text_layout::Line],
1305 cx: &mut ViewContext<Self>,
1306 ) -> bool {
1307 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1308 let selections = self.selections.all::<Point>(cx);
1309
1310 let mut target_left;
1311 let mut target_right;
1312
1313 if self.highlighted_rows.is_some() {
1314 target_left = 0.0_f32;
1315 target_right = 0.0_f32;
1316 } else {
1317 target_left = std::f32::INFINITY;
1318 target_right = 0.0_f32;
1319 for selection in selections {
1320 let head = selection.head().to_display_point(&display_map);
1321 if head.row() >= start_row && head.row() < start_row + layouts.len() as u32 {
1322 let start_column = head.column().saturating_sub(3);
1323 let end_column = cmp::min(display_map.line_len(head.row()), head.column() + 3);
1324 target_left = target_left.min(
1325 layouts[(head.row() - start_row) as usize]
1326 .x_for_index(start_column as usize),
1327 );
1328 target_right = target_right.max(
1329 layouts[(head.row() - start_row) as usize].x_for_index(end_column as usize)
1330 + max_glyph_width,
1331 );
1332 }
1333 }
1334 }
1335
1336 target_right = target_right.min(scroll_width);
1337
1338 if target_right - target_left > viewport_width {
1339 return false;
1340 }
1341
1342 let scroll_left = self.scroll_position.x() * max_glyph_width;
1343 let scroll_right = scroll_left + viewport_width;
1344
1345 if target_left < scroll_left {
1346 self.scroll_position.set_x(target_left / max_glyph_width);
1347 true
1348 } else if target_right > scroll_right {
1349 self.scroll_position
1350 .set_x((target_right - viewport_width) / max_glyph_width);
1351 true
1352 } else {
1353 false
1354 }
1355 }
1356
1357 fn selections_did_change(
1358 &mut self,
1359 local: bool,
1360 old_cursor_position: &Anchor,
1361 cx: &mut ViewContext<Self>,
1362 ) {
1363 if self.focused && self.leader_replica_id.is_none() {
1364 self.buffer.update(cx, |buffer, cx| {
1365 buffer.set_active_selections(
1366 &self.selections.disjoint_anchors(),
1367 self.selections.line_mode,
1368 cx,
1369 )
1370 });
1371 }
1372
1373 let display_map = self
1374 .display_map
1375 .update(cx, |display_map, cx| display_map.snapshot(cx));
1376 let buffer = &display_map.buffer_snapshot;
1377 self.add_selections_state = None;
1378 self.select_next_state = None;
1379 self.select_larger_syntax_node_stack.clear();
1380 self.autoclose_stack
1381 .invalidate(&self.selections.disjoint_anchors(), buffer);
1382 self.snippet_stack
1383 .invalidate(&self.selections.disjoint_anchors(), buffer);
1384 self.take_rename(false, cx);
1385
1386 let new_cursor_position = self.selections.newest_anchor().head();
1387
1388 self.push_to_nav_history(
1389 old_cursor_position.clone(),
1390 Some(new_cursor_position.to_point(buffer)),
1391 cx,
1392 );
1393
1394 if local {
1395 let new_cursor_position = self.selections.newest_anchor().head();
1396 let completion_menu = match self.context_menu.as_mut() {
1397 Some(ContextMenu::Completions(menu)) => Some(menu),
1398 _ => {
1399 self.context_menu.take();
1400 None
1401 }
1402 };
1403
1404 if let Some(completion_menu) = completion_menu {
1405 let cursor_position = new_cursor_position.to_offset(buffer);
1406 let (word_range, kind) =
1407 buffer.surrounding_word(completion_menu.initial_position.clone());
1408 if kind == Some(CharKind::Word)
1409 && word_range.to_inclusive().contains(&cursor_position)
1410 {
1411 let query = Self::completion_query(buffer, cursor_position);
1412 cx.background()
1413 .block(completion_menu.filter(query.as_deref(), cx.background().clone()));
1414 self.show_completions(&ShowCompletions, cx);
1415 } else {
1416 self.hide_context_menu(cx);
1417 }
1418 }
1419
1420 hide_hover(self, cx);
1421
1422 if old_cursor_position.to_display_point(&display_map).row()
1423 != new_cursor_position.to_display_point(&display_map).row()
1424 {
1425 self.available_code_actions.take();
1426 }
1427 self.refresh_code_actions(cx);
1428 self.refresh_document_highlights(cx);
1429 refresh_matching_bracket_highlights(self, cx);
1430 }
1431
1432 self.pause_cursor_blinking(cx);
1433 cx.emit(Event::SelectionsChanged { local });
1434 cx.notify();
1435 }
1436
1437 pub fn change_selections<R>(
1438 &mut self,
1439 autoscroll: Option<Autoscroll>,
1440 cx: &mut ViewContext<Self>,
1441 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
1442 ) -> R {
1443 let old_cursor_position = self.selections.newest_anchor().head();
1444 self.push_to_selection_history();
1445
1446 let (changed, result) = self.selections.change_with(cx, change);
1447
1448 if changed {
1449 if let Some(autoscroll) = autoscroll {
1450 self.request_autoscroll(autoscroll, cx);
1451 }
1452 self.selections_did_change(true, &old_cursor_position, cx);
1453 }
1454
1455 result
1456 }
1457
1458 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
1459 where
1460 I: IntoIterator<Item = (Range<S>, T)>,
1461 S: ToOffset,
1462 T: Into<Arc<str>>,
1463 {
1464 self.buffer.update(cx, |buffer, cx| buffer.edit(edits, cx));
1465 }
1466
1467 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
1468 where
1469 I: IntoIterator<Item = (Range<S>, T)>,
1470 S: ToOffset,
1471 T: Into<Arc<str>>,
1472 {
1473 self.buffer
1474 .update(cx, |buffer, cx| buffer.edit_with_autoindent(edits, cx));
1475 }
1476
1477 fn select(&mut self, Select(phase): &Select, cx: &mut ViewContext<Self>) {
1478 self.hide_context_menu(cx);
1479
1480 match phase {
1481 SelectPhase::Begin {
1482 position,
1483 add,
1484 click_count,
1485 } => self.begin_selection(*position, *add, *click_count, cx),
1486 SelectPhase::BeginColumnar {
1487 position,
1488 overshoot,
1489 } => self.begin_columnar_selection(*position, *overshoot, cx),
1490 SelectPhase::Extend {
1491 position,
1492 click_count,
1493 } => self.extend_selection(*position, *click_count, cx),
1494 SelectPhase::Update {
1495 position,
1496 overshoot,
1497 scroll_position,
1498 } => self.update_selection(*position, *overshoot, *scroll_position, cx),
1499 SelectPhase::End => self.end_selection(cx),
1500 }
1501 }
1502
1503 fn extend_selection(
1504 &mut self,
1505 position: DisplayPoint,
1506 click_count: usize,
1507 cx: &mut ViewContext<Self>,
1508 ) {
1509 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1510 let tail = self.selections.newest::<usize>(cx).tail();
1511 self.begin_selection(position, false, click_count, cx);
1512
1513 let position = position.to_offset(&display_map, Bias::Left);
1514 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
1515
1516 let mut pending_selection = self
1517 .selections
1518 .pending_anchor()
1519 .expect("extend_selection not called with pending selection");
1520 if position >= tail {
1521 pending_selection.start = tail_anchor.clone();
1522 } else {
1523 pending_selection.end = tail_anchor.clone();
1524 pending_selection.reversed = true;
1525 }
1526
1527 let mut pending_mode = self.selections.pending_mode().unwrap();
1528 match &mut pending_mode {
1529 SelectMode::Word(range) | SelectMode::Line(range) => {
1530 *range = tail_anchor.clone()..tail_anchor
1531 }
1532 _ => {}
1533 }
1534
1535 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
1536 s.set_pending(pending_selection, pending_mode)
1537 });
1538 }
1539
1540 fn begin_selection(
1541 &mut self,
1542 position: DisplayPoint,
1543 add: bool,
1544 click_count: usize,
1545 cx: &mut ViewContext<Self>,
1546 ) {
1547 if !self.focused {
1548 cx.focus_self();
1549 cx.emit(Event::Activate);
1550 }
1551
1552 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1553 let buffer = &display_map.buffer_snapshot;
1554 let newest_selection = self.selections.newest_anchor().clone();
1555 let position = display_map.clip_point(position, Bias::Left);
1556
1557 let start;
1558 let end;
1559 let mode;
1560 match click_count {
1561 1 => {
1562 start = buffer.anchor_before(position.to_point(&display_map));
1563 end = start.clone();
1564 mode = SelectMode::Character;
1565 }
1566 2 => {
1567 let range = movement::surrounding_word(&display_map, position);
1568 start = buffer.anchor_before(range.start.to_point(&display_map));
1569 end = buffer.anchor_before(range.end.to_point(&display_map));
1570 mode = SelectMode::Word(start.clone()..end.clone());
1571 }
1572 3 => {
1573 let position = display_map
1574 .clip_point(position, Bias::Left)
1575 .to_point(&display_map);
1576 let line_start = display_map.prev_line_boundary(position).0;
1577 let next_line_start = buffer.clip_point(
1578 display_map.next_line_boundary(position).0 + Point::new(1, 0),
1579 Bias::Left,
1580 );
1581 start = buffer.anchor_before(line_start);
1582 end = buffer.anchor_before(next_line_start);
1583 mode = SelectMode::Line(start.clone()..end.clone());
1584 }
1585 _ => {
1586 start = buffer.anchor_before(0);
1587 end = buffer.anchor_before(buffer.len());
1588 mode = SelectMode::All;
1589 }
1590 }
1591
1592 self.change_selections(Some(Autoscroll::Newest), cx, |s| {
1593 if !add {
1594 s.clear_disjoint();
1595 } else if click_count > 1 {
1596 s.delete(newest_selection.id)
1597 }
1598
1599 s.set_pending_range(start..end, mode);
1600 });
1601 }
1602
1603 fn begin_columnar_selection(
1604 &mut self,
1605 position: DisplayPoint,
1606 overshoot: u32,
1607 cx: &mut ViewContext<Self>,
1608 ) {
1609 if !self.focused {
1610 cx.focus_self();
1611 cx.emit(Event::Activate);
1612 }
1613
1614 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1615 let tail = self.selections.newest::<Point>(cx).tail();
1616 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
1617
1618 self.select_columns(
1619 tail.to_display_point(&display_map),
1620 position,
1621 overshoot,
1622 &display_map,
1623 cx,
1624 );
1625 }
1626
1627 fn update_selection(
1628 &mut self,
1629 position: DisplayPoint,
1630 overshoot: u32,
1631 scroll_position: Vector2F,
1632 cx: &mut ViewContext<Self>,
1633 ) {
1634 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1635
1636 if let Some(tail) = self.columnar_selection_tail.as_ref() {
1637 let tail = tail.to_display_point(&display_map);
1638 self.select_columns(tail, position, overshoot, &display_map, cx);
1639 } else if let Some(mut pending) = self.selections.pending_anchor().clone() {
1640 let buffer = self.buffer.read(cx).snapshot(cx);
1641 let head;
1642 let tail;
1643 let mode = self.selections.pending_mode().unwrap();
1644 match &mode {
1645 SelectMode::Character => {
1646 head = position.to_point(&display_map);
1647 tail = pending.tail().to_point(&buffer);
1648 }
1649 SelectMode::Word(original_range) => {
1650 let original_display_range = original_range.start.to_display_point(&display_map)
1651 ..original_range.end.to_display_point(&display_map);
1652 let original_buffer_range = original_display_range.start.to_point(&display_map)
1653 ..original_display_range.end.to_point(&display_map);
1654 if movement::is_inside_word(&display_map, position)
1655 || original_display_range.contains(&position)
1656 {
1657 let word_range = movement::surrounding_word(&display_map, position);
1658 if word_range.start < original_display_range.start {
1659 head = word_range.start.to_point(&display_map);
1660 } else {
1661 head = word_range.end.to_point(&display_map);
1662 }
1663 } else {
1664 head = position.to_point(&display_map);
1665 }
1666
1667 if head <= original_buffer_range.start {
1668 tail = original_buffer_range.end;
1669 } else {
1670 tail = original_buffer_range.start;
1671 }
1672 }
1673 SelectMode::Line(original_range) => {
1674 let original_range = original_range.to_point(&display_map.buffer_snapshot);
1675
1676 let position = display_map
1677 .clip_point(position, Bias::Left)
1678 .to_point(&display_map);
1679 let line_start = display_map.prev_line_boundary(position).0;
1680 let next_line_start = buffer.clip_point(
1681 display_map.next_line_boundary(position).0 + Point::new(1, 0),
1682 Bias::Left,
1683 );
1684
1685 if line_start < original_range.start {
1686 head = line_start
1687 } else {
1688 head = next_line_start
1689 }
1690
1691 if head <= original_range.start {
1692 tail = original_range.end;
1693 } else {
1694 tail = original_range.start;
1695 }
1696 }
1697 SelectMode::All => {
1698 return;
1699 }
1700 };
1701
1702 if head < tail {
1703 pending.start = buffer.anchor_before(head);
1704 pending.end = buffer.anchor_before(tail);
1705 pending.reversed = true;
1706 } else {
1707 pending.start = buffer.anchor_before(tail);
1708 pending.end = buffer.anchor_before(head);
1709 pending.reversed = false;
1710 }
1711
1712 self.change_selections(None, cx, |s| {
1713 s.set_pending(pending, mode);
1714 });
1715 } else {
1716 log::error!("update_selection dispatched with no pending selection");
1717 return;
1718 }
1719
1720 self.set_scroll_position(scroll_position, cx);
1721 cx.notify();
1722 }
1723
1724 fn end_selection(&mut self, cx: &mut ViewContext<Self>) {
1725 self.columnar_selection_tail.take();
1726 if self.selections.pending_anchor().is_some() {
1727 let selections = self.selections.all::<usize>(cx);
1728 self.change_selections(None, cx, |s| {
1729 s.select(selections);
1730 s.clear_pending();
1731 });
1732 }
1733 }
1734
1735 fn select_columns(
1736 &mut self,
1737 tail: DisplayPoint,
1738 head: DisplayPoint,
1739 overshoot: u32,
1740 display_map: &DisplaySnapshot,
1741 cx: &mut ViewContext<Self>,
1742 ) {
1743 let start_row = cmp::min(tail.row(), head.row());
1744 let end_row = cmp::max(tail.row(), head.row());
1745 let start_column = cmp::min(tail.column(), head.column() + overshoot);
1746 let end_column = cmp::max(tail.column(), head.column() + overshoot);
1747 let reversed = start_column < tail.column();
1748
1749 let selection_ranges = (start_row..=end_row)
1750 .filter_map(|row| {
1751 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
1752 let start = display_map
1753 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
1754 .to_point(&display_map);
1755 let end = display_map
1756 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
1757 .to_point(&display_map);
1758 if reversed {
1759 Some(end..start)
1760 } else {
1761 Some(start..end)
1762 }
1763 } else {
1764 None
1765 }
1766 })
1767 .collect::<Vec<_>>();
1768
1769 self.change_selections(None, cx, |s| {
1770 s.select_ranges(selection_ranges);
1771 });
1772 cx.notify();
1773 }
1774
1775 pub fn is_selecting(&self) -> bool {
1776 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
1777 }
1778
1779 pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
1780 if self.take_rename(false, cx).is_some() {
1781 return;
1782 }
1783
1784 if hide_hover(self, cx) {
1785 return;
1786 }
1787
1788 if self.hide_context_menu(cx).is_some() {
1789 return;
1790 }
1791
1792 if self.snippet_stack.pop().is_some() {
1793 return;
1794 }
1795
1796 if self.mode == EditorMode::Full {
1797 if self.active_diagnostics.is_some() {
1798 self.dismiss_diagnostics(cx);
1799 return;
1800 }
1801
1802 if self.change_selections(Some(Autoscroll::Fit), cx, |s| s.try_cancel()) {
1803 return;
1804 }
1805 }
1806
1807 cx.propagate_action();
1808 }
1809
1810 pub fn handle_input(&mut self, action: &Input, cx: &mut ViewContext<Self>) {
1811 if !self.input_enabled {
1812 cx.propagate_action();
1813 return;
1814 }
1815
1816 let text = action.0.as_ref();
1817 if !self.skip_autoclose_end(text, cx) {
1818 self.transact(cx, |this, cx| {
1819 if !this.surround_with_bracket_pair(text, cx) {
1820 this.insert(text, cx);
1821 this.autoclose_bracket_pairs(cx);
1822 }
1823 });
1824 self.trigger_completion_on_input(text, cx);
1825 }
1826 }
1827
1828 pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
1829 self.transact(cx, |this, cx| {
1830 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
1831 let selections = this.selections.all::<usize>(cx);
1832
1833 let buffer = this.buffer.read(cx).snapshot(cx);
1834 selections
1835 .iter()
1836 .map(|selection| {
1837 let start_point = selection.start.to_point(&buffer);
1838 let mut indent = buffer.indent_size_for_line(start_point.row);
1839 indent.len = cmp::min(indent.len, start_point.column);
1840 let start = selection.start;
1841 let end = selection.end;
1842
1843 let mut insert_extra_newline = false;
1844 if let Some(language) = buffer.language() {
1845 let leading_whitespace_len = buffer
1846 .reversed_chars_at(start)
1847 .take_while(|c| c.is_whitespace() && *c != '\n')
1848 .map(|c| c.len_utf8())
1849 .sum::<usize>();
1850
1851 let trailing_whitespace_len = buffer
1852 .chars_at(end)
1853 .take_while(|c| c.is_whitespace() && *c != '\n')
1854 .map(|c| c.len_utf8())
1855 .sum::<usize>();
1856
1857 insert_extra_newline = language.brackets().iter().any(|pair| {
1858 let pair_start = pair.start.trim_end();
1859 let pair_end = pair.end.trim_start();
1860
1861 pair.newline
1862 && buffer
1863 .contains_str_at(end + trailing_whitespace_len, pair_end)
1864 && buffer.contains_str_at(
1865 (start - leading_whitespace_len)
1866 .saturating_sub(pair_start.len()),
1867 pair_start,
1868 )
1869 });
1870 }
1871
1872 let mut new_text = String::with_capacity(1 + indent.len as usize);
1873 new_text.push('\n');
1874 new_text.extend(indent.chars());
1875 if insert_extra_newline {
1876 new_text = new_text.repeat(2);
1877 }
1878
1879 let anchor = buffer.anchor_after(end);
1880 let new_selection = selection.map(|_| anchor.clone());
1881 (
1882 (start..end, new_text),
1883 (insert_extra_newline, new_selection),
1884 )
1885 })
1886 .unzip()
1887 };
1888
1889 this.buffer.update(cx, |buffer, cx| {
1890 buffer.edit_with_autoindent(edits, cx);
1891 });
1892 let buffer = this.buffer.read(cx).snapshot(cx);
1893 let new_selections = selection_fixup_info
1894 .into_iter()
1895 .map(|(extra_newline_inserted, new_selection)| {
1896 let mut cursor = new_selection.end.to_point(&buffer);
1897 if extra_newline_inserted {
1898 cursor.row -= 1;
1899 cursor.column = buffer.line_len(cursor.row);
1900 }
1901 new_selection.map(|_| cursor.clone())
1902 })
1903 .collect();
1904
1905 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(new_selections));
1906 });
1907 }
1908
1909 pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
1910 let text: Arc<str> = text.into();
1911 self.transact(cx, |this, cx| {
1912 let old_selections = this.selections.all_adjusted(cx);
1913 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
1914 let anchors = {
1915 let snapshot = buffer.read(cx);
1916 old_selections
1917 .iter()
1918 .map(|s| {
1919 let anchor = snapshot.anchor_after(s.end);
1920 s.map(|_| anchor.clone())
1921 })
1922 .collect::<Vec<_>>()
1923 };
1924 buffer.edit_with_autoindent(
1925 old_selections
1926 .iter()
1927 .map(|s| (s.start..s.end, text.clone())),
1928 cx,
1929 );
1930 anchors
1931 });
1932
1933 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
1934 s.select_anchors(selection_anchors);
1935 })
1936 });
1937 }
1938
1939 fn trigger_completion_on_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
1940 if !cx.global::<Settings>().show_completions_on_input {
1941 return;
1942 }
1943
1944 let selection = self.selections.newest_anchor();
1945 if self
1946 .buffer
1947 .read(cx)
1948 .is_completion_trigger(selection.head(), text, cx)
1949 {
1950 self.show_completions(&ShowCompletions, cx);
1951 } else {
1952 self.hide_context_menu(cx);
1953 }
1954 }
1955
1956 fn surround_with_bracket_pair(&mut self, text: &str, cx: &mut ViewContext<Self>) -> bool {
1957 let snapshot = self.buffer.read(cx).snapshot(cx);
1958 if let Some(pair) = snapshot
1959 .language()
1960 .and_then(|language| language.brackets().iter().find(|b| b.start == text))
1961 .cloned()
1962 {
1963 if self
1964 .selections
1965 .all::<usize>(cx)
1966 .iter()
1967 .any(|selection| selection.is_empty())
1968 {
1969 return false;
1970 }
1971
1972 let mut selections = self.selections.disjoint_anchors().to_vec();
1973 for selection in &mut selections {
1974 selection.end = selection.end.bias_left(&snapshot);
1975 }
1976 drop(snapshot);
1977
1978 self.buffer.update(cx, |buffer, cx| {
1979 let pair_start: Arc<str> = pair.start.clone().into();
1980 let pair_end: Arc<str> = pair.end.clone().into();
1981 buffer.edit(
1982 selections.iter().flat_map(|s| {
1983 [
1984 (s.start.clone()..s.start.clone(), pair_start.clone()),
1985 (s.end.clone()..s.end.clone(), pair_end.clone()),
1986 ]
1987 }),
1988 cx,
1989 );
1990 });
1991
1992 let snapshot = self.buffer.read(cx).read(cx);
1993 for selection in &mut selections {
1994 selection.end = selection.end.bias_right(&snapshot);
1995 }
1996 drop(snapshot);
1997
1998 self.change_selections(None, cx, |s| s.select_anchors(selections));
1999 true
2000 } else {
2001 false
2002 }
2003 }
2004
2005 fn autoclose_bracket_pairs(&mut self, cx: &mut ViewContext<Self>) {
2006 let selections = self.selections.all::<usize>(cx);
2007 let mut bracket_pair_state = None;
2008 let mut new_selections = None;
2009 self.buffer.update(cx, |buffer, cx| {
2010 let mut snapshot = buffer.snapshot(cx);
2011 let left_biased_selections = selections
2012 .iter()
2013 .map(|selection| selection.map(|p| snapshot.anchor_before(p)))
2014 .collect::<Vec<_>>();
2015
2016 let autoclose_pair = snapshot.language().and_then(|language| {
2017 let first_selection_start = selections.first().unwrap().start;
2018 let pair = language.brackets().iter().find(|pair| {
2019 pair.close
2020 && snapshot.contains_str_at(
2021 first_selection_start.saturating_sub(pair.start.len()),
2022 &pair.start,
2023 )
2024 });
2025 pair.and_then(|pair| {
2026 let should_autoclose = selections.iter().all(|selection| {
2027 // Ensure all selections are parked at the end of a pair start.
2028 if snapshot.contains_str_at(
2029 selection.start.saturating_sub(pair.start.len()),
2030 &pair.start,
2031 ) {
2032 snapshot
2033 .chars_at(selection.start)
2034 .next()
2035 .map_or(true, |c| language.should_autoclose_before(c))
2036 } else {
2037 false
2038 }
2039 });
2040
2041 if should_autoclose {
2042 Some(pair.clone())
2043 } else {
2044 None
2045 }
2046 })
2047 });
2048
2049 if let Some(pair) = autoclose_pair {
2050 let selection_ranges = selections
2051 .iter()
2052 .map(|selection| {
2053 let start = selection.start.to_offset(&snapshot);
2054 start..start
2055 })
2056 .collect::<SmallVec<[_; 32]>>();
2057
2058 let pair_end: Arc<str> = pair.end.clone().into();
2059 buffer.edit(
2060 selection_ranges
2061 .iter()
2062 .map(|range| (range.clone(), pair_end.clone())),
2063 cx,
2064 );
2065 snapshot = buffer.snapshot(cx);
2066
2067 new_selections = Some(
2068 resolve_multiple::<usize, _>(left_biased_selections.iter(), &snapshot)
2069 .collect::<Vec<_>>(),
2070 );
2071
2072 if pair.end.len() == 1 {
2073 let mut delta = 0;
2074 bracket_pair_state = Some(BracketPairState {
2075 ranges: selections
2076 .iter()
2077 .map(move |selection| {
2078 let offset = selection.start + delta;
2079 delta += 1;
2080 snapshot.anchor_before(offset)..snapshot.anchor_after(offset)
2081 })
2082 .collect(),
2083 pair,
2084 });
2085 }
2086 }
2087 });
2088
2089 if let Some(new_selections) = new_selections {
2090 self.change_selections(None, cx, |s| {
2091 s.select(new_selections);
2092 });
2093 }
2094 if let Some(bracket_pair_state) = bracket_pair_state {
2095 self.autoclose_stack.push(bracket_pair_state);
2096 }
2097 }
2098
2099 fn skip_autoclose_end(&mut self, text: &str, cx: &mut ViewContext<Self>) -> bool {
2100 let buffer = self.buffer.read(cx).snapshot(cx);
2101 let old_selections = self.selections.all::<usize>(cx);
2102 let autoclose_pair = if let Some(autoclose_pair) = self.autoclose_stack.last() {
2103 autoclose_pair
2104 } else {
2105 return false;
2106 };
2107 if text != autoclose_pair.pair.end {
2108 return false;
2109 }
2110
2111 debug_assert_eq!(old_selections.len(), autoclose_pair.ranges.len());
2112
2113 if old_selections
2114 .iter()
2115 .zip(autoclose_pair.ranges.iter().map(|r| r.to_offset(&buffer)))
2116 .all(|(selection, autoclose_range)| {
2117 let autoclose_range_end = autoclose_range.end.to_offset(&buffer);
2118 selection.is_empty() && selection.start == autoclose_range_end
2119 })
2120 {
2121 let new_selections = old_selections
2122 .into_iter()
2123 .map(|selection| {
2124 let cursor = selection.start + 1;
2125 Selection {
2126 id: selection.id,
2127 start: cursor,
2128 end: cursor,
2129 reversed: false,
2130 goal: SelectionGoal::None,
2131 }
2132 })
2133 .collect();
2134 self.autoclose_stack.pop();
2135 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
2136 s.select(new_selections);
2137 });
2138 true
2139 } else {
2140 false
2141 }
2142 }
2143
2144 fn select_autoclose_pair(&mut self, cx: &mut ViewContext<Self>) -> bool {
2145 let buffer = self.buffer.read(cx).snapshot(cx);
2146 let old_selections = self.selections.all::<usize>(cx);
2147 let autoclose_pair = if let Some(autoclose_pair) = self.autoclose_stack.last() {
2148 autoclose_pair
2149 } else {
2150 return false;
2151 };
2152
2153 debug_assert_eq!(old_selections.len(), autoclose_pair.ranges.len());
2154
2155 let mut new_selections = Vec::new();
2156 for (selection, autoclose_range) in old_selections
2157 .iter()
2158 .zip(autoclose_pair.ranges.iter().map(|r| r.to_offset(&buffer)))
2159 {
2160 if selection.is_empty()
2161 && autoclose_range.is_empty()
2162 && selection.start == autoclose_range.start
2163 {
2164 new_selections.push(Selection {
2165 id: selection.id,
2166 start: selection.start - autoclose_pair.pair.start.len(),
2167 end: selection.end + autoclose_pair.pair.end.len(),
2168 reversed: true,
2169 goal: selection.goal,
2170 });
2171 } else {
2172 return false;
2173 }
2174 }
2175
2176 self.change_selections(Some(Autoscroll::Fit), cx, |selections| {
2177 selections.select(new_selections)
2178 });
2179 true
2180 }
2181
2182 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
2183 let offset = position.to_offset(buffer);
2184 let (word_range, kind) = buffer.surrounding_word(offset);
2185 if offset > word_range.start && kind == Some(CharKind::Word) {
2186 Some(
2187 buffer
2188 .text_for_range(word_range.start..offset)
2189 .collect::<String>(),
2190 )
2191 } else {
2192 None
2193 }
2194 }
2195
2196 fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) {
2197 if self.pending_rename.is_some() {
2198 return;
2199 }
2200
2201 let project = if let Some(project) = self.project.clone() {
2202 project
2203 } else {
2204 return;
2205 };
2206
2207 let position = self.selections.newest_anchor().head();
2208 let (buffer, buffer_position) = if let Some(output) = self
2209 .buffer
2210 .read(cx)
2211 .text_anchor_for_position(position.clone(), cx)
2212 {
2213 output
2214 } else {
2215 return;
2216 };
2217
2218 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position.clone());
2219 let completions = project.update(cx, |project, cx| {
2220 project.completions(&buffer, buffer_position.clone(), cx)
2221 });
2222
2223 let id = post_inc(&mut self.next_completion_id);
2224 let task = cx.spawn_weak(|this, mut cx| {
2225 async move {
2226 let completions = completions.await?;
2227 if completions.is_empty() {
2228 return Ok(());
2229 }
2230
2231 let mut menu = CompletionsMenu {
2232 id,
2233 initial_position: position,
2234 match_candidates: completions
2235 .iter()
2236 .enumerate()
2237 .map(|(id, completion)| {
2238 StringMatchCandidate::new(
2239 id,
2240 completion.label.text[completion.label.filter_range.clone()].into(),
2241 )
2242 })
2243 .collect(),
2244 buffer,
2245 completions: completions.into(),
2246 matches: Vec::new().into(),
2247 selected_item: 0,
2248 list: Default::default(),
2249 };
2250
2251 menu.filter(query.as_deref(), cx.background()).await;
2252
2253 if let Some(this) = this.upgrade(&cx) {
2254 this.update(&mut cx, |this, cx| {
2255 match this.context_menu.as_ref() {
2256 None => {}
2257 Some(ContextMenu::Completions(prev_menu)) => {
2258 if prev_menu.id > menu.id {
2259 return;
2260 }
2261 }
2262 _ => return,
2263 }
2264
2265 this.completion_tasks.retain(|(id, _)| *id > menu.id);
2266 if this.focused {
2267 this.show_context_menu(ContextMenu::Completions(menu), cx);
2268 }
2269
2270 cx.notify();
2271 });
2272 }
2273 Ok::<_, anyhow::Error>(())
2274 }
2275 .log_err()
2276 });
2277 self.completion_tasks.push((id, task));
2278 }
2279
2280 pub fn confirm_completion(
2281 &mut self,
2282 action: &ConfirmCompletion,
2283 cx: &mut ViewContext<Self>,
2284 ) -> Option<Task<Result<()>>> {
2285 use language::ToOffset as _;
2286
2287 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
2288 menu
2289 } else {
2290 return None;
2291 };
2292
2293 let mat = completions_menu
2294 .matches
2295 .get(action.item_ix.unwrap_or(completions_menu.selected_item))?;
2296 let buffer_handle = completions_menu.buffer;
2297 let completion = completions_menu.completions.get(mat.candidate_id)?;
2298
2299 let snippet;
2300 let text;
2301 if completion.is_snippet() {
2302 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
2303 text = snippet.as_ref().unwrap().text.clone();
2304 } else {
2305 snippet = None;
2306 text = completion.new_text.clone();
2307 };
2308 let selections = self.selections.all::<usize>(cx);
2309 let buffer = buffer_handle.read(cx);
2310 let old_range = completion.old_range.to_offset(&buffer);
2311 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
2312
2313 let newest_selection = self.selections.newest_anchor();
2314 if newest_selection.start.buffer_id != Some(buffer_handle.id()) {
2315 return None;
2316 }
2317
2318 let lookbehind = newest_selection
2319 .start
2320 .text_anchor
2321 .to_offset(buffer)
2322 .saturating_sub(old_range.start);
2323 let lookahead = old_range
2324 .end
2325 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
2326 let mut common_prefix_len = old_text
2327 .bytes()
2328 .zip(text.bytes())
2329 .take_while(|(a, b)| a == b)
2330 .count();
2331
2332 let snapshot = self.buffer.read(cx).snapshot(cx);
2333 let mut ranges = Vec::new();
2334 for selection in &selections {
2335 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
2336 let start = selection.start.saturating_sub(lookbehind);
2337 let end = selection.end + lookahead;
2338 ranges.push(start + common_prefix_len..end);
2339 } else {
2340 common_prefix_len = 0;
2341 ranges.clear();
2342 ranges.extend(selections.iter().map(|s| {
2343 if s.id == newest_selection.id {
2344 old_range.clone()
2345 } else {
2346 s.start..s.end
2347 }
2348 }));
2349 break;
2350 }
2351 }
2352 let text = &text[common_prefix_len..];
2353
2354 self.transact(cx, |this, cx| {
2355 if let Some(mut snippet) = snippet {
2356 snippet.text = text.to_string();
2357 for tabstop in snippet.tabstops.iter_mut().flatten() {
2358 tabstop.start -= common_prefix_len as isize;
2359 tabstop.end -= common_prefix_len as isize;
2360 }
2361
2362 this.insert_snippet(&ranges, snippet, cx).log_err();
2363 } else {
2364 this.buffer.update(cx, |buffer, cx| {
2365 buffer
2366 .edit_with_autoindent(ranges.iter().map(|range| (range.clone(), text)), cx);
2367 });
2368 }
2369 });
2370
2371 let project = self.project.clone()?;
2372 let apply_edits = project.update(cx, |project, cx| {
2373 project.apply_additional_edits_for_completion(
2374 buffer_handle,
2375 completion.clone(),
2376 true,
2377 cx,
2378 )
2379 });
2380 Some(cx.foreground().spawn(async move {
2381 apply_edits.await?;
2382 Ok(())
2383 }))
2384 }
2385
2386 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
2387 if matches!(
2388 self.context_menu.as_ref(),
2389 Some(ContextMenu::CodeActions(_))
2390 ) {
2391 self.context_menu.take();
2392 cx.notify();
2393 return;
2394 }
2395
2396 let deployed_from_indicator = action.deployed_from_indicator;
2397 let mut task = self.code_actions_task.take();
2398 cx.spawn_weak(|this, mut cx| async move {
2399 while let Some(prev_task) = task {
2400 prev_task.await;
2401 task = this
2402 .upgrade(&cx)
2403 .and_then(|this| this.update(&mut cx, |this, _| this.code_actions_task.take()));
2404 }
2405
2406 if let Some(this) = this.upgrade(&cx) {
2407 this.update(&mut cx, |this, cx| {
2408 if this.focused {
2409 if let Some((buffer, actions)) = this.available_code_actions.clone() {
2410 this.show_context_menu(
2411 ContextMenu::CodeActions(CodeActionsMenu {
2412 buffer,
2413 actions,
2414 selected_item: Default::default(),
2415 list: Default::default(),
2416 deployed_from_indicator,
2417 }),
2418 cx,
2419 );
2420 }
2421 }
2422 })
2423 }
2424 Ok::<_, anyhow::Error>(())
2425 })
2426 .detach_and_log_err(cx);
2427 }
2428
2429 pub fn confirm_code_action(
2430 workspace: &mut Workspace,
2431 action: &ConfirmCodeAction,
2432 cx: &mut ViewContext<Workspace>,
2433 ) -> Option<Task<Result<()>>> {
2434 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
2435 let actions_menu = if let ContextMenu::CodeActions(menu) =
2436 editor.update(cx, |editor, cx| editor.hide_context_menu(cx))?
2437 {
2438 menu
2439 } else {
2440 return None;
2441 };
2442 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
2443 let action = actions_menu.actions.get(action_ix)?.clone();
2444 let title = action.lsp_action.title.clone();
2445 let buffer = actions_menu.buffer;
2446
2447 let apply_code_actions = workspace.project().clone().update(cx, |project, cx| {
2448 project.apply_code_action(buffer, action, true, cx)
2449 });
2450 Some(cx.spawn(|workspace, cx| async move {
2451 let project_transaction = apply_code_actions.await?;
2452 Self::open_project_transaction(editor, workspace, project_transaction, title, cx).await
2453 }))
2454 }
2455
2456 async fn open_project_transaction(
2457 this: ViewHandle<Editor>,
2458 workspace: ViewHandle<Workspace>,
2459 transaction: ProjectTransaction,
2460 title: String,
2461 mut cx: AsyncAppContext,
2462 ) -> Result<()> {
2463 let replica_id = this.read_with(&cx, |this, cx| this.replica_id(cx));
2464
2465 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
2466 entries.sort_unstable_by_key(|(buffer, _)| {
2467 buffer.read_with(&cx, |buffer, _| buffer.file().map(|f| f.path().clone()))
2468 });
2469
2470 // If the project transaction's edits are all contained within this editor, then
2471 // avoid opening a new editor to display them.
2472
2473 if let Some((buffer, transaction)) = entries.first() {
2474 if entries.len() == 1 {
2475 let excerpt = this.read_with(&cx, |editor, cx| {
2476 editor
2477 .buffer()
2478 .read(cx)
2479 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
2480 });
2481 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
2482 if excerpted_buffer == *buffer {
2483 let snapshot = buffer.read_with(&cx, |buffer, _| buffer.snapshot());
2484 let excerpt_range = excerpt_range.to_offset(&snapshot);
2485 if snapshot
2486 .edited_ranges_for_transaction(transaction)
2487 .all(|range| {
2488 excerpt_range.start <= range.start && excerpt_range.end >= range.end
2489 })
2490 {
2491 return Ok(());
2492 }
2493 }
2494 }
2495 }
2496 } else {
2497 return Ok(());
2498 }
2499
2500 let mut ranges_to_highlight = Vec::new();
2501 let excerpt_buffer = cx.add_model(|cx| {
2502 let mut multibuffer = MultiBuffer::new(replica_id).with_title(title);
2503 for (buffer, transaction) in &entries {
2504 let snapshot = buffer.read(cx).snapshot();
2505 ranges_to_highlight.extend(
2506 multibuffer.push_excerpts_with_context_lines(
2507 buffer.clone(),
2508 snapshot
2509 .edited_ranges_for_transaction::<usize>(transaction)
2510 .collect(),
2511 1,
2512 cx,
2513 ),
2514 );
2515 }
2516 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)));
2517 multibuffer
2518 });
2519
2520 workspace.update(&mut cx, |workspace, cx| {
2521 let project = workspace.project().clone();
2522 let editor =
2523 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
2524 workspace.add_item(Box::new(editor.clone()), cx);
2525 editor.update(cx, |editor, cx| {
2526 editor.highlight_background::<Self>(
2527 ranges_to_highlight,
2528 |theme| theme.editor.highlighted_line_background,
2529 cx,
2530 );
2531 });
2532 });
2533
2534 Ok(())
2535 }
2536
2537 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
2538 let project = self.project.as_ref()?;
2539 let buffer = self.buffer.read(cx);
2540 let newest_selection = self.selections.newest_anchor().clone();
2541 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
2542 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
2543 if start_buffer != end_buffer {
2544 return None;
2545 }
2546
2547 let actions = project.update(cx, |project, cx| {
2548 project.code_actions(&start_buffer, start..end, cx)
2549 });
2550 self.code_actions_task = Some(cx.spawn_weak(|this, mut cx| async move {
2551 let actions = actions.await;
2552 if let Some(this) = this.upgrade(&cx) {
2553 this.update(&mut cx, |this, cx| {
2554 this.available_code_actions = actions.log_err().and_then(|actions| {
2555 if actions.is_empty() {
2556 None
2557 } else {
2558 Some((start_buffer, actions.into()))
2559 }
2560 });
2561 cx.notify();
2562 })
2563 }
2564 }));
2565 None
2566 }
2567
2568 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
2569 if self.pending_rename.is_some() {
2570 return None;
2571 }
2572
2573 let project = self.project.as_ref()?;
2574 let buffer = self.buffer.read(cx);
2575 let newest_selection = self.selections.newest_anchor().clone();
2576 let cursor_position = newest_selection.head();
2577 let (cursor_buffer, cursor_buffer_position) =
2578 buffer.text_anchor_for_position(cursor_position.clone(), cx)?;
2579 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
2580 if cursor_buffer != tail_buffer {
2581 return None;
2582 }
2583
2584 let highlights = project.update(cx, |project, cx| {
2585 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
2586 });
2587
2588 self.document_highlights_task = Some(cx.spawn_weak(|this, mut cx| async move {
2589 let highlights = highlights.log_err().await;
2590 if let Some((this, highlights)) = this.upgrade(&cx).zip(highlights) {
2591 this.update(&mut cx, |this, cx| {
2592 if this.pending_rename.is_some() {
2593 return;
2594 }
2595
2596 let buffer_id = cursor_position.buffer_id;
2597 let buffer = this.buffer.read(cx);
2598 if !buffer
2599 .text_anchor_for_position(cursor_position, cx)
2600 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
2601 {
2602 return;
2603 }
2604
2605 let cursor_buffer_snapshot = cursor_buffer.read(cx);
2606 let mut write_ranges = Vec::new();
2607 let mut read_ranges = Vec::new();
2608 for highlight in highlights {
2609 for (excerpt_id, excerpt_range) in
2610 buffer.excerpts_for_buffer(&cursor_buffer, cx)
2611 {
2612 let start = highlight
2613 .range
2614 .start
2615 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
2616 let end = highlight
2617 .range
2618 .end
2619 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
2620 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
2621 continue;
2622 }
2623
2624 let range = Anchor {
2625 buffer_id,
2626 excerpt_id: excerpt_id.clone(),
2627 text_anchor: start,
2628 }..Anchor {
2629 buffer_id,
2630 excerpt_id,
2631 text_anchor: end,
2632 };
2633 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
2634 write_ranges.push(range);
2635 } else {
2636 read_ranges.push(range);
2637 }
2638 }
2639 }
2640
2641 this.highlight_background::<DocumentHighlightRead>(
2642 read_ranges,
2643 |theme| theme.editor.document_highlight_read_background,
2644 cx,
2645 );
2646 this.highlight_background::<DocumentHighlightWrite>(
2647 write_ranges,
2648 |theme| theme.editor.document_highlight_write_background,
2649 cx,
2650 );
2651 cx.notify();
2652 });
2653 }
2654 }));
2655 None
2656 }
2657
2658 pub fn render_code_actions_indicator(
2659 &self,
2660 style: &EditorStyle,
2661 cx: &mut RenderContext<Self>,
2662 ) -> Option<ElementBox> {
2663 if self.available_code_actions.is_some() {
2664 enum Tag {}
2665 Some(
2666 MouseEventHandler::new::<Tag, _, _>(0, cx, |_, _| {
2667 Svg::new("icons/zap.svg")
2668 .with_color(style.code_actions_indicator)
2669 .boxed()
2670 })
2671 .with_cursor_style(CursorStyle::PointingHand)
2672 .with_padding(Padding::uniform(3.))
2673 .on_mouse_down(|_, cx| {
2674 cx.dispatch_action(ToggleCodeActions {
2675 deployed_from_indicator: true,
2676 });
2677 })
2678 .boxed(),
2679 )
2680 } else {
2681 None
2682 }
2683 }
2684
2685 pub fn context_menu_visible(&self) -> bool {
2686 self.context_menu
2687 .as_ref()
2688 .map_or(false, |menu| menu.visible())
2689 }
2690
2691 pub fn render_context_menu(
2692 &self,
2693 cursor_position: DisplayPoint,
2694 style: EditorStyle,
2695 cx: &mut RenderContext<Editor>,
2696 ) -> Option<(DisplayPoint, ElementBox)> {
2697 self.context_menu
2698 .as_ref()
2699 .map(|menu| menu.render(cursor_position, style, cx))
2700 }
2701
2702 fn show_context_menu(&mut self, menu: ContextMenu, cx: &mut ViewContext<Self>) {
2703 if !matches!(menu, ContextMenu::Completions(_)) {
2704 self.completion_tasks.clear();
2705 }
2706 self.context_menu = Some(menu);
2707 cx.notify();
2708 }
2709
2710 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
2711 cx.notify();
2712 self.completion_tasks.clear();
2713 self.context_menu.take()
2714 }
2715
2716 pub fn insert_snippet(
2717 &mut self,
2718 insertion_ranges: &[Range<usize>],
2719 snippet: Snippet,
2720 cx: &mut ViewContext<Self>,
2721 ) -> Result<()> {
2722 let tabstops = self.buffer.update(cx, |buffer, cx| {
2723 let snippet_text: Arc<str> = snippet.text.clone().into();
2724 buffer.edit_with_autoindent(
2725 insertion_ranges
2726 .iter()
2727 .cloned()
2728 .map(|range| (range, snippet_text.clone())),
2729 cx,
2730 );
2731
2732 let snapshot = &*buffer.read(cx);
2733 let snippet = &snippet;
2734 snippet
2735 .tabstops
2736 .iter()
2737 .map(|tabstop| {
2738 let mut tabstop_ranges = tabstop
2739 .iter()
2740 .flat_map(|tabstop_range| {
2741 let mut delta = 0 as isize;
2742 insertion_ranges.iter().map(move |insertion_range| {
2743 let insertion_start = insertion_range.start as isize + delta;
2744 delta +=
2745 snippet.text.len() as isize - insertion_range.len() as isize;
2746
2747 let start = snapshot.anchor_before(
2748 (insertion_start + tabstop_range.start) as usize,
2749 );
2750 let end = snapshot
2751 .anchor_after((insertion_start + tabstop_range.end) as usize);
2752 start..end
2753 })
2754 })
2755 .collect::<Vec<_>>();
2756 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
2757 tabstop_ranges
2758 })
2759 .collect::<Vec<_>>()
2760 });
2761
2762 if let Some(tabstop) = tabstops.first() {
2763 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
2764 s.select_ranges(tabstop.iter().cloned());
2765 });
2766 self.snippet_stack.push(SnippetState {
2767 active_index: 0,
2768 ranges: tabstops,
2769 });
2770 }
2771
2772 Ok(())
2773 }
2774
2775 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
2776 self.move_to_snippet_tabstop(Bias::Right, cx)
2777 }
2778
2779 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
2780 self.move_to_snippet_tabstop(Bias::Left, cx)
2781 }
2782
2783 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
2784 if let Some(mut snippet) = self.snippet_stack.pop() {
2785 match bias {
2786 Bias::Left => {
2787 if snippet.active_index > 0 {
2788 snippet.active_index -= 1;
2789 } else {
2790 self.snippet_stack.push(snippet);
2791 return false;
2792 }
2793 }
2794 Bias::Right => {
2795 if snippet.active_index + 1 < snippet.ranges.len() {
2796 snippet.active_index += 1;
2797 } else {
2798 self.snippet_stack.push(snippet);
2799 return false;
2800 }
2801 }
2802 }
2803 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
2804 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
2805 s.select_anchor_ranges(current_ranges.into_iter().cloned())
2806 });
2807 // If snippet state is not at the last tabstop, push it back on the stack
2808 if snippet.active_index + 1 < snippet.ranges.len() {
2809 self.snippet_stack.push(snippet);
2810 }
2811 return true;
2812 }
2813 }
2814
2815 false
2816 }
2817
2818 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
2819 self.transact(cx, |this, cx| {
2820 this.select_all(&SelectAll, cx);
2821 this.insert("", cx);
2822 });
2823 }
2824
2825 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
2826 self.transact(cx, |this, cx| {
2827 if !this.select_autoclose_pair(cx) {
2828 let mut selections = this.selections.all::<Point>(cx);
2829 if !this.selections.line_mode {
2830 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
2831 for selection in &mut selections {
2832 if selection.is_empty() {
2833 let old_head = selection.head();
2834 let mut new_head = movement::left(
2835 &display_map,
2836 old_head.to_display_point(&display_map),
2837 )
2838 .to_point(&display_map);
2839 if let Some((buffer, line_buffer_range)) = display_map
2840 .buffer_snapshot
2841 .buffer_line_for_row(old_head.row)
2842 {
2843 let indent_size =
2844 buffer.indent_size_for_line(line_buffer_range.start.row);
2845 let language_name =
2846 buffer.language().map(|language| language.name());
2847 let indent_len = match indent_size.kind {
2848 IndentKind::Space => {
2849 cx.global::<Settings>().tab_size(language_name.as_deref())
2850 }
2851 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
2852 };
2853 if old_head.column <= indent_size.len && old_head.column > 0 {
2854 let indent_len = indent_len.get();
2855 new_head = cmp::min(
2856 new_head,
2857 Point::new(
2858 old_head.row,
2859 ((old_head.column - 1) / indent_len) * indent_len,
2860 ),
2861 );
2862 }
2863 }
2864
2865 selection.set_head(new_head, SelectionGoal::None);
2866 }
2867 }
2868 }
2869
2870 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
2871 }
2872 this.insert("", cx);
2873 });
2874 }
2875
2876 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
2877 self.transact(cx, |this, cx| {
2878 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
2879 let line_mode = s.line_mode;
2880 s.move_with(|map, selection| {
2881 if selection.is_empty() && !line_mode {
2882 let cursor = movement::right(map, selection.head());
2883 selection.set_head(cursor, SelectionGoal::None);
2884 }
2885 })
2886 });
2887 this.insert(&"", cx);
2888 });
2889 }
2890
2891 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
2892 if self.move_to_prev_snippet_tabstop(cx) {
2893 return;
2894 }
2895
2896 self.outdent(&Outdent, cx);
2897 }
2898
2899 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
2900 if self.move_to_next_snippet_tabstop(cx) {
2901 return;
2902 }
2903
2904 let mut selections = self.selections.all_adjusted(cx);
2905 if selections.iter().all(|s| s.is_empty()) {
2906 self.transact(cx, |this, cx| {
2907 this.buffer.update(cx, |buffer, cx| {
2908 for selection in &mut selections {
2909 let language_name =
2910 buffer.language_at(selection.start, cx).map(|l| l.name());
2911 let settings = cx.global::<Settings>();
2912 let tab_size = if settings.hard_tabs(language_name.as_deref()) {
2913 IndentSize::tab()
2914 } else {
2915 let tab_size = settings.tab_size(language_name.as_deref()).get();
2916 let char_column = buffer
2917 .read(cx)
2918 .text_for_range(Point::new(selection.start.row, 0)..selection.start)
2919 .flat_map(str::chars)
2920 .count();
2921 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
2922 IndentSize::spaces(chars_to_next_tab_stop)
2923 };
2924 buffer.edit(
2925 [(
2926 selection.start..selection.start,
2927 tab_size.chars().collect::<String>(),
2928 )],
2929 cx,
2930 );
2931 selection.start.column += tab_size.len;
2932 selection.end = selection.start;
2933 }
2934 });
2935 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
2936 s.select(selections);
2937 });
2938 });
2939 } else {
2940 self.indent(&Indent, cx);
2941 }
2942 }
2943
2944 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
2945 let mut selections = self.selections.all::<Point>(cx);
2946 self.transact(cx, |this, cx| {
2947 let mut last_indent = None;
2948 this.buffer.update(cx, |buffer, cx| {
2949 let snapshot = buffer.snapshot(cx);
2950 for selection in &mut selections {
2951 let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
2952 let settings = &cx.global::<Settings>();
2953 let tab_size = settings.tab_size(language_name.as_deref()).get();
2954 let indent_kind = if settings.hard_tabs(language_name.as_deref()) {
2955 IndentKind::Tab
2956 } else {
2957 IndentKind::Space
2958 };
2959
2960 let mut start_row = selection.start.row;
2961 let mut end_row = selection.end.row + 1;
2962
2963 // If a selection ends at the beginning of a line, don't indent
2964 // that last line.
2965 if selection.end.column == 0 {
2966 end_row -= 1;
2967 }
2968
2969 // Avoid re-indenting a row that has already been indented by a
2970 // previous selection, but still update this selection's column
2971 // to reflect that indentation.
2972 if let Some((last_indent_row, last_indent_len)) = last_indent {
2973 if last_indent_row == selection.start.row {
2974 selection.start.column += last_indent_len;
2975 start_row += 1;
2976 }
2977 if last_indent_row == selection.end.row {
2978 selection.end.column += last_indent_len;
2979 }
2980 }
2981
2982 for row in start_row..end_row {
2983 let current_indent = snapshot.indent_size_for_line(row);
2984 let indent_delta = match (current_indent.kind, indent_kind) {
2985 (IndentKind::Space, IndentKind::Space) => {
2986 let columns_to_next_tab_stop =
2987 tab_size - (current_indent.len % tab_size);
2988 IndentSize::spaces(columns_to_next_tab_stop)
2989 }
2990 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
2991 (_, IndentKind::Tab) => IndentSize::tab(),
2992 };
2993
2994 let row_start = Point::new(row, 0);
2995 buffer.edit(
2996 [(
2997 row_start..row_start,
2998 indent_delta.chars().collect::<String>(),
2999 )],
3000 cx,
3001 );
3002
3003 // Update this selection's endpoints to reflect the indentation.
3004 if row == selection.start.row {
3005 selection.start.column += indent_delta.len;
3006 }
3007 if row == selection.end.row {
3008 selection.end.column += indent_delta.len as u32;
3009 }
3010
3011 last_indent = Some((row, indent_delta.len));
3012 }
3013 }
3014 });
3015
3016 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3017 s.select(selections);
3018 });
3019 });
3020 }
3021
3022 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
3023 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3024 let selections = self.selections.all::<Point>(cx);
3025 let mut deletion_ranges = Vec::new();
3026 let mut last_outdent = None;
3027 {
3028 let buffer = self.buffer.read(cx);
3029 let snapshot = buffer.snapshot(cx);
3030 for selection in &selections {
3031 let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
3032 let tab_size = cx
3033 .global::<Settings>()
3034 .tab_size(language_name.as_deref())
3035 .get();
3036 let mut rows = selection.spanned_rows(false, &display_map);
3037
3038 // Avoid re-outdenting a row that has already been outdented by a
3039 // previous selection.
3040 if let Some(last_row) = last_outdent {
3041 if last_row == rows.start {
3042 rows.start += 1;
3043 }
3044 }
3045
3046 for row in rows {
3047 let indent_size = snapshot.indent_size_for_line(row);
3048 if indent_size.len > 0 {
3049 let deletion_len = match indent_size.kind {
3050 IndentKind::Space => {
3051 let columns_to_prev_tab_stop = indent_size.len % tab_size;
3052 if columns_to_prev_tab_stop == 0 {
3053 tab_size
3054 } else {
3055 columns_to_prev_tab_stop
3056 }
3057 }
3058 IndentKind::Tab => 1,
3059 };
3060 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
3061 last_outdent = Some(row);
3062 }
3063 }
3064 }
3065 }
3066
3067 self.transact(cx, |this, cx| {
3068 this.buffer.update(cx, |buffer, cx| {
3069 let empty_str: Arc<str> = "".into();
3070 buffer.edit(
3071 deletion_ranges
3072 .into_iter()
3073 .map(|range| (range, empty_str.clone())),
3074 cx,
3075 );
3076 });
3077 let selections = this.selections.all::<usize>(cx);
3078 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
3079 });
3080 }
3081
3082 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
3083 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3084 let selections = self.selections.all::<Point>(cx);
3085
3086 let mut new_cursors = Vec::new();
3087 let mut edit_ranges = Vec::new();
3088 let mut selections = selections.iter().peekable();
3089 while let Some(selection) = selections.next() {
3090 let mut rows = selection.spanned_rows(false, &display_map);
3091 let goal_display_column = selection.head().to_display_point(&display_map).column();
3092
3093 // Accumulate contiguous regions of rows that we want to delete.
3094 while let Some(next_selection) = selections.peek() {
3095 let next_rows = next_selection.spanned_rows(false, &display_map);
3096 if next_rows.start <= rows.end {
3097 rows.end = next_rows.end;
3098 selections.next().unwrap();
3099 } else {
3100 break;
3101 }
3102 }
3103
3104 let buffer = &display_map.buffer_snapshot;
3105 let mut edit_start = Point::new(rows.start, 0).to_offset(&buffer);
3106 let edit_end;
3107 let cursor_buffer_row;
3108 if buffer.max_point().row >= rows.end {
3109 // If there's a line after the range, delete the \n from the end of the row range
3110 // and position the cursor on the next line.
3111 edit_end = Point::new(rows.end, 0).to_offset(&buffer);
3112 cursor_buffer_row = rows.end;
3113 } else {
3114 // If there isn't a line after the range, delete the \n from the line before the
3115 // start of the row range and position the cursor there.
3116 edit_start = edit_start.saturating_sub(1);
3117 edit_end = buffer.len();
3118 cursor_buffer_row = rows.start.saturating_sub(1);
3119 }
3120
3121 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
3122 *cursor.column_mut() =
3123 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
3124
3125 new_cursors.push((
3126 selection.id,
3127 buffer.anchor_after(cursor.to_point(&display_map)),
3128 ));
3129 edit_ranges.push(edit_start..edit_end);
3130 }
3131
3132 self.transact(cx, |this, cx| {
3133 let buffer = this.buffer.update(cx, |buffer, cx| {
3134 let empty_str: Arc<str> = "".into();
3135 buffer.edit(
3136 edit_ranges
3137 .into_iter()
3138 .map(|range| (range, empty_str.clone())),
3139 cx,
3140 );
3141 buffer.snapshot(cx)
3142 });
3143 let new_selections = new_cursors
3144 .into_iter()
3145 .map(|(id, cursor)| {
3146 let cursor = cursor.to_point(&buffer);
3147 Selection {
3148 id,
3149 start: cursor,
3150 end: cursor,
3151 reversed: false,
3152 goal: SelectionGoal::None,
3153 }
3154 })
3155 .collect();
3156
3157 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3158 s.select(new_selections);
3159 });
3160 });
3161 }
3162
3163 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
3164 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3165 let buffer = &display_map.buffer_snapshot;
3166 let selections = self.selections.all::<Point>(cx);
3167
3168 let mut edits = Vec::new();
3169 let mut selections_iter = selections.iter().peekable();
3170 while let Some(selection) = selections_iter.next() {
3171 // Avoid duplicating the same lines twice.
3172 let mut rows = selection.spanned_rows(false, &display_map);
3173
3174 while let Some(next_selection) = selections_iter.peek() {
3175 let next_rows = next_selection.spanned_rows(false, &display_map);
3176 if next_rows.start <= rows.end - 1 {
3177 rows.end = next_rows.end;
3178 selections_iter.next().unwrap();
3179 } else {
3180 break;
3181 }
3182 }
3183
3184 // Copy the text from the selected row region and splice it at the start of the region.
3185 let start = Point::new(rows.start, 0);
3186 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
3187 let text = buffer
3188 .text_for_range(start..end)
3189 .chain(Some("\n"))
3190 .collect::<String>();
3191 edits.push((start..start, text));
3192 }
3193
3194 self.transact(cx, |this, cx| {
3195 this.buffer.update(cx, |buffer, cx| {
3196 buffer.edit(edits, cx);
3197 });
3198
3199 this.request_autoscroll(Autoscroll::Fit, cx);
3200 });
3201 }
3202
3203 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
3204 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3205 let buffer = self.buffer.read(cx).snapshot(cx);
3206
3207 let mut edits = Vec::new();
3208 let mut unfold_ranges = Vec::new();
3209 let mut refold_ranges = Vec::new();
3210
3211 let selections = self.selections.all::<Point>(cx);
3212 let mut selections = selections.iter().peekable();
3213 let mut contiguous_row_selections = Vec::new();
3214 let mut new_selections = Vec::new();
3215
3216 while let Some(selection) = selections.next() {
3217 // Find all the selections that span a contiguous row range
3218 contiguous_row_selections.push(selection.clone());
3219 let start_row = selection.start.row;
3220 let mut end_row = if selection.end.column > 0 || selection.is_empty() {
3221 display_map.next_line_boundary(selection.end).0.row + 1
3222 } else {
3223 selection.end.row
3224 };
3225
3226 while let Some(next_selection) = selections.peek() {
3227 if next_selection.start.row <= end_row {
3228 end_row = if next_selection.end.column > 0 || next_selection.is_empty() {
3229 display_map.next_line_boundary(next_selection.end).0.row + 1
3230 } else {
3231 next_selection.end.row
3232 };
3233 contiguous_row_selections.push(selections.next().unwrap().clone());
3234 } else {
3235 break;
3236 }
3237 }
3238
3239 // Move the text spanned by the row range to be before the line preceding the row range
3240 if start_row > 0 {
3241 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
3242 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
3243 let insertion_point = display_map
3244 .prev_line_boundary(Point::new(start_row - 1, 0))
3245 .0;
3246
3247 // Don't move lines across excerpts
3248 if buffer
3249 .excerpt_boundaries_in_range((
3250 Bound::Excluded(insertion_point),
3251 Bound::Included(range_to_move.end),
3252 ))
3253 .next()
3254 .is_none()
3255 {
3256 let text = buffer
3257 .text_for_range(range_to_move.clone())
3258 .flat_map(|s| s.chars())
3259 .skip(1)
3260 .chain(['\n'])
3261 .collect::<String>();
3262
3263 edits.push((
3264 buffer.anchor_after(range_to_move.start)
3265 ..buffer.anchor_before(range_to_move.end),
3266 String::new(),
3267 ));
3268 let insertion_anchor = buffer.anchor_after(insertion_point);
3269 edits.push((insertion_anchor.clone()..insertion_anchor, text));
3270
3271 let row_delta = range_to_move.start.row - insertion_point.row + 1;
3272
3273 // Move selections up
3274 new_selections.extend(contiguous_row_selections.drain(..).map(
3275 |mut selection| {
3276 selection.start.row -= row_delta;
3277 selection.end.row -= row_delta;
3278 selection
3279 },
3280 ));
3281
3282 // Move folds up
3283 unfold_ranges.push(range_to_move.clone());
3284 for fold in display_map.folds_in_range(
3285 buffer.anchor_before(range_to_move.start)
3286 ..buffer.anchor_after(range_to_move.end),
3287 ) {
3288 let mut start = fold.start.to_point(&buffer);
3289 let mut end = fold.end.to_point(&buffer);
3290 start.row -= row_delta;
3291 end.row -= row_delta;
3292 refold_ranges.push(start..end);
3293 }
3294 }
3295 }
3296
3297 // If we didn't move line(s), preserve the existing selections
3298 new_selections.extend(contiguous_row_selections.drain(..));
3299 }
3300
3301 self.transact(cx, |this, cx| {
3302 this.unfold_ranges(unfold_ranges, true, cx);
3303 this.buffer.update(cx, |buffer, cx| {
3304 for (range, text) in edits {
3305 buffer.edit([(range, text)], cx);
3306 }
3307 });
3308 this.fold_ranges(refold_ranges, cx);
3309 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3310 s.select(new_selections);
3311 })
3312 });
3313 }
3314
3315 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
3316 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3317 let buffer = self.buffer.read(cx).snapshot(cx);
3318
3319 let mut edits = Vec::new();
3320 let mut unfold_ranges = Vec::new();
3321 let mut refold_ranges = Vec::new();
3322
3323 let selections = self.selections.all::<Point>(cx);
3324 let mut selections = selections.iter().peekable();
3325 let mut contiguous_row_selections = Vec::new();
3326 let mut new_selections = Vec::new();
3327
3328 while let Some(selection) = selections.next() {
3329 // Find all the selections that span a contiguous row range
3330 contiguous_row_selections.push(selection.clone());
3331 let start_row = selection.start.row;
3332 let mut end_row = if selection.end.column > 0 || selection.is_empty() {
3333 display_map.next_line_boundary(selection.end).0.row + 1
3334 } else {
3335 selection.end.row
3336 };
3337
3338 while let Some(next_selection) = selections.peek() {
3339 if next_selection.start.row <= end_row {
3340 end_row = if next_selection.end.column > 0 || next_selection.is_empty() {
3341 display_map.next_line_boundary(next_selection.end).0.row + 1
3342 } else {
3343 next_selection.end.row
3344 };
3345 contiguous_row_selections.push(selections.next().unwrap().clone());
3346 } else {
3347 break;
3348 }
3349 }
3350
3351 // Move the text spanned by the row range to be after the last line of the row range
3352 if end_row <= buffer.max_point().row {
3353 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
3354 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
3355
3356 // Don't move lines across excerpt boundaries
3357 if buffer
3358 .excerpt_boundaries_in_range((
3359 Bound::Excluded(range_to_move.start),
3360 Bound::Included(insertion_point),
3361 ))
3362 .next()
3363 .is_none()
3364 {
3365 let mut text = String::from("\n");
3366 text.extend(buffer.text_for_range(range_to_move.clone()));
3367 text.pop(); // Drop trailing newline
3368 edits.push((
3369 buffer.anchor_after(range_to_move.start)
3370 ..buffer.anchor_before(range_to_move.end),
3371 String::new(),
3372 ));
3373 let insertion_anchor = buffer.anchor_after(insertion_point);
3374 edits.push((insertion_anchor.clone()..insertion_anchor, text));
3375
3376 let row_delta = insertion_point.row - range_to_move.end.row + 1;
3377
3378 // Move selections down
3379 new_selections.extend(contiguous_row_selections.drain(..).map(
3380 |mut selection| {
3381 selection.start.row += row_delta;
3382 selection.end.row += row_delta;
3383 selection
3384 },
3385 ));
3386
3387 // Move folds down
3388 unfold_ranges.push(range_to_move.clone());
3389 for fold in display_map.folds_in_range(
3390 buffer.anchor_before(range_to_move.start)
3391 ..buffer.anchor_after(range_to_move.end),
3392 ) {
3393 let mut start = fold.start.to_point(&buffer);
3394 let mut end = fold.end.to_point(&buffer);
3395 start.row += row_delta;
3396 end.row += row_delta;
3397 refold_ranges.push(start..end);
3398 }
3399 }
3400 }
3401
3402 // If we didn't move line(s), preserve the existing selections
3403 new_selections.extend(contiguous_row_selections.drain(..));
3404 }
3405
3406 self.transact(cx, |this, cx| {
3407 this.unfold_ranges(unfold_ranges, true, cx);
3408 this.buffer.update(cx, |buffer, cx| {
3409 for (range, text) in edits {
3410 buffer.edit([(range, text)], cx);
3411 }
3412 });
3413 this.fold_ranges(refold_ranges, cx);
3414 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(new_selections));
3415 });
3416 }
3417
3418 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
3419 self.transact(cx, |this, cx| {
3420 let edits = this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3421 let mut edits: Vec<(Range<usize>, String)> = Default::default();
3422 let line_mode = s.line_mode;
3423 s.move_with(|display_map, selection| {
3424 if !selection.is_empty() || line_mode {
3425 return;
3426 }
3427
3428 let mut head = selection.head();
3429 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
3430 if head.column() == display_map.line_len(head.row()) {
3431 transpose_offset = display_map
3432 .buffer_snapshot
3433 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
3434 }
3435
3436 if transpose_offset == 0 {
3437 return;
3438 }
3439
3440 *head.column_mut() += 1;
3441 head = display_map.clip_point(head, Bias::Right);
3442 selection.collapse_to(head, SelectionGoal::Column(head.column()));
3443
3444 let transpose_start = display_map
3445 .buffer_snapshot
3446 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
3447 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
3448 let transpose_end = display_map
3449 .buffer_snapshot
3450 .clip_offset(transpose_offset + 1, Bias::Right);
3451 if let Some(ch) =
3452 display_map.buffer_snapshot.chars_at(transpose_start).next()
3453 {
3454 edits.push((transpose_start..transpose_offset, String::new()));
3455 edits.push((transpose_end..transpose_end, ch.to_string()));
3456 }
3457 }
3458 });
3459 edits
3460 });
3461 this.buffer.update(cx, |buffer, cx| buffer.edit(edits, cx));
3462 let selections = this.selections.all::<usize>(cx);
3463 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3464 s.select(selections);
3465 });
3466 });
3467 }
3468
3469 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
3470 let mut text = String::new();
3471 let buffer = self.buffer.read(cx).snapshot(cx);
3472 let mut selections = self.selections.all::<Point>(cx);
3473 let mut clipboard_selections = Vec::with_capacity(selections.len());
3474 {
3475 let max_point = buffer.max_point();
3476 for selection in &mut selections {
3477 let is_entire_line = selection.is_empty() || self.selections.line_mode;
3478 if is_entire_line {
3479 selection.start = Point::new(selection.start.row, 0);
3480 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
3481 selection.goal = SelectionGoal::None;
3482 }
3483 let mut len = 0;
3484 for chunk in buffer.text_for_range(selection.start..selection.end) {
3485 text.push_str(chunk);
3486 len += chunk.len();
3487 }
3488 clipboard_selections.push(ClipboardSelection {
3489 len,
3490 is_entire_line,
3491 });
3492 }
3493 }
3494
3495 self.transact(cx, |this, cx| {
3496 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3497 s.select(selections);
3498 });
3499 this.insert("", cx);
3500 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
3501 });
3502 }
3503
3504 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
3505 let selections = self.selections.all::<Point>(cx);
3506 let buffer = self.buffer.read(cx).read(cx);
3507 let mut text = String::new();
3508
3509 let mut clipboard_selections = Vec::with_capacity(selections.len());
3510 {
3511 let max_point = buffer.max_point();
3512 for selection in selections.iter() {
3513 let mut start = selection.start;
3514 let mut end = selection.end;
3515 let is_entire_line = selection.is_empty() || self.selections.line_mode;
3516 if is_entire_line {
3517 start = Point::new(start.row, 0);
3518 end = cmp::min(max_point, Point::new(end.row + 1, 0));
3519 }
3520 let mut len = 0;
3521 for chunk in buffer.text_for_range(start..end) {
3522 text.push_str(chunk);
3523 len += chunk.len();
3524 }
3525 clipboard_selections.push(ClipboardSelection {
3526 len,
3527 is_entire_line,
3528 });
3529 }
3530 }
3531
3532 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
3533 }
3534
3535 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
3536 self.transact(cx, |this, cx| {
3537 if let Some(item) = cx.as_mut().read_from_clipboard() {
3538 let mut clipboard_text = Cow::Borrowed(item.text());
3539 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
3540 let old_selections = this.selections.all::<usize>(cx);
3541 let all_selections_were_entire_line =
3542 clipboard_selections.iter().all(|s| s.is_entire_line);
3543 if clipboard_selections.len() != old_selections.len() {
3544 let mut newline_separated_text = String::new();
3545 let mut clipboard_selections = clipboard_selections.drain(..).peekable();
3546 let mut ix = 0;
3547 while let Some(clipboard_selection) = clipboard_selections.next() {
3548 newline_separated_text
3549 .push_str(&clipboard_text[ix..ix + clipboard_selection.len]);
3550 ix += clipboard_selection.len;
3551 if clipboard_selections.peek().is_some() {
3552 newline_separated_text.push('\n');
3553 }
3554 }
3555 clipboard_text = Cow::Owned(newline_separated_text);
3556 }
3557
3558 this.buffer.update(cx, |buffer, cx| {
3559 let snapshot = buffer.read(cx);
3560 let mut start_offset = 0;
3561 let mut edits = Vec::new();
3562 let line_mode = this.selections.line_mode;
3563 for (ix, selection) in old_selections.iter().enumerate() {
3564 let to_insert;
3565 let entire_line;
3566 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
3567 let end_offset = start_offset + clipboard_selection.len;
3568 to_insert = &clipboard_text[start_offset..end_offset];
3569 entire_line = clipboard_selection.is_entire_line;
3570 start_offset = end_offset;
3571 } else {
3572 to_insert = clipboard_text.as_str();
3573 entire_line = all_selections_were_entire_line;
3574 }
3575
3576 // If the corresponding selection was empty when this slice of the
3577 // clipboard text was written, then the entire line containing the
3578 // selection was copied. If this selection is also currently empty,
3579 // then paste the line before the current line of the buffer.
3580 let range = if selection.is_empty() && !line_mode && entire_line {
3581 let column = selection.start.to_point(&snapshot).column as usize;
3582 let line_start = selection.start - column;
3583 line_start..line_start
3584 } else {
3585 selection.range()
3586 };
3587
3588 edits.push((range, to_insert));
3589 }
3590 drop(snapshot);
3591 buffer.edit_with_autoindent(edits, cx);
3592 });
3593
3594 let selections = this.selections.all::<usize>(cx);
3595 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
3596 } else {
3597 this.insert(&clipboard_text, cx);
3598 }
3599 }
3600 });
3601 }
3602
3603 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
3604 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
3605 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
3606 self.change_selections(None, cx, |s| {
3607 s.select_anchors(selections.to_vec());
3608 });
3609 }
3610 self.request_autoscroll(Autoscroll::Fit, cx);
3611 cx.emit(Event::Edited);
3612 }
3613 }
3614
3615 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
3616 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
3617 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
3618 {
3619 self.change_selections(None, cx, |s| {
3620 s.select_anchors(selections.to_vec());
3621 });
3622 }
3623 self.request_autoscroll(Autoscroll::Fit, cx);
3624 cx.emit(Event::Edited);
3625 }
3626 }
3627
3628 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
3629 self.buffer
3630 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
3631 }
3632
3633 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
3634 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3635 let line_mode = s.line_mode;
3636 s.move_with(|map, selection| {
3637 let cursor = if selection.is_empty() && !line_mode {
3638 movement::left(map, selection.start)
3639 } else {
3640 selection.start
3641 };
3642 selection.collapse_to(cursor, SelectionGoal::None);
3643 });
3644 })
3645 }
3646
3647 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
3648 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3649 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
3650 })
3651 }
3652
3653 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
3654 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3655 let line_mode = s.line_mode;
3656 s.move_with(|map, selection| {
3657 let cursor = if selection.is_empty() && !line_mode {
3658 movement::right(map, selection.end)
3659 } else {
3660 selection.end
3661 };
3662 selection.collapse_to(cursor, SelectionGoal::None)
3663 });
3664 })
3665 }
3666
3667 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
3668 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3669 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
3670 })
3671 }
3672
3673 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
3674 if self.take_rename(true, cx).is_some() {
3675 return;
3676 }
3677
3678 if let Some(context_menu) = self.context_menu.as_mut() {
3679 if context_menu.select_prev(cx) {
3680 return;
3681 }
3682 }
3683
3684 if matches!(self.mode, EditorMode::SingleLine) {
3685 cx.propagate_action();
3686 return;
3687 }
3688
3689 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3690 let line_mode = s.line_mode;
3691 s.move_with(|map, selection| {
3692 if !selection.is_empty() && !line_mode {
3693 selection.goal = SelectionGoal::None;
3694 }
3695 let (cursor, goal) = movement::up(&map, selection.start, selection.goal, false);
3696 selection.collapse_to(cursor, goal);
3697 });
3698 })
3699 }
3700
3701 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
3702 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3703 s.move_heads_with(|map, head, goal| movement::up(map, head, goal, false))
3704 })
3705 }
3706
3707 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
3708 self.take_rename(true, cx);
3709
3710 if let Some(context_menu) = self.context_menu.as_mut() {
3711 if context_menu.select_next(cx) {
3712 return;
3713 }
3714 }
3715
3716 if matches!(self.mode, EditorMode::SingleLine) {
3717 cx.propagate_action();
3718 return;
3719 }
3720
3721 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3722 let line_mode = s.line_mode;
3723 s.move_with(|map, selection| {
3724 if !selection.is_empty() && !line_mode {
3725 selection.goal = SelectionGoal::None;
3726 }
3727 let (cursor, goal) = movement::down(&map, selection.end, selection.goal, false);
3728 selection.collapse_to(cursor, goal);
3729 });
3730 });
3731 }
3732
3733 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
3734 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3735 s.move_heads_with(|map, head, goal| movement::down(map, head, goal, false))
3736 });
3737 }
3738
3739 pub fn move_to_previous_word_start(
3740 &mut self,
3741 _: &MoveToPreviousWordStart,
3742 cx: &mut ViewContext<Self>,
3743 ) {
3744 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3745 s.move_cursors_with(|map, head, _| {
3746 (
3747 movement::previous_word_start(map, head),
3748 SelectionGoal::None,
3749 )
3750 });
3751 })
3752 }
3753
3754 pub fn move_to_previous_subword_start(
3755 &mut self,
3756 _: &MoveToPreviousSubwordStart,
3757 cx: &mut ViewContext<Self>,
3758 ) {
3759 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3760 s.move_cursors_with(|map, head, _| {
3761 (
3762 movement::previous_subword_start(map, head),
3763 SelectionGoal::None,
3764 )
3765 });
3766 })
3767 }
3768
3769 pub fn select_to_previous_word_start(
3770 &mut self,
3771 _: &SelectToPreviousWordStart,
3772 cx: &mut ViewContext<Self>,
3773 ) {
3774 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3775 s.move_heads_with(|map, head, _| {
3776 (
3777 movement::previous_word_start(map, head),
3778 SelectionGoal::None,
3779 )
3780 });
3781 })
3782 }
3783
3784 pub fn select_to_previous_subword_start(
3785 &mut self,
3786 _: &SelectToPreviousSubwordStart,
3787 cx: &mut ViewContext<Self>,
3788 ) {
3789 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3790 s.move_heads_with(|map, head, _| {
3791 (
3792 movement::previous_subword_start(map, head),
3793 SelectionGoal::None,
3794 )
3795 });
3796 })
3797 }
3798
3799 pub fn delete_to_previous_word_start(
3800 &mut self,
3801 _: &DeleteToPreviousWordStart,
3802 cx: &mut ViewContext<Self>,
3803 ) {
3804 self.transact(cx, |this, cx| {
3805 if !this.select_autoclose_pair(cx) {
3806 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3807 let line_mode = s.line_mode;
3808 s.move_with(|map, selection| {
3809 if selection.is_empty() && !line_mode {
3810 let cursor = movement::previous_word_start(map, selection.head());
3811 selection.set_head(cursor, SelectionGoal::None);
3812 }
3813 });
3814 });
3815 }
3816 this.insert("", cx);
3817 });
3818 }
3819
3820 pub fn delete_to_previous_subword_start(
3821 &mut self,
3822 _: &DeleteToPreviousSubwordStart,
3823 cx: &mut ViewContext<Self>,
3824 ) {
3825 self.transact(cx, |this, cx| {
3826 if !this.select_autoclose_pair(cx) {
3827 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3828 let line_mode = s.line_mode;
3829 s.move_with(|map, selection| {
3830 if selection.is_empty() && !line_mode {
3831 let cursor = movement::previous_subword_start(map, selection.head());
3832 selection.set_head(cursor, SelectionGoal::None);
3833 }
3834 });
3835 });
3836 }
3837 this.insert("", cx);
3838 });
3839 }
3840
3841 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
3842 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3843 s.move_cursors_with(|map, head, _| {
3844 (movement::next_word_end(map, head), SelectionGoal::None)
3845 });
3846 })
3847 }
3848
3849 pub fn move_to_next_subword_end(
3850 &mut self,
3851 _: &MoveToNextSubwordEnd,
3852 cx: &mut ViewContext<Self>,
3853 ) {
3854 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3855 s.move_cursors_with(|map, head, _| {
3856 (movement::next_subword_end(map, head), SelectionGoal::None)
3857 });
3858 })
3859 }
3860
3861 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
3862 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3863 s.move_heads_with(|map, head, _| {
3864 (movement::next_word_end(map, head), SelectionGoal::None)
3865 });
3866 })
3867 }
3868
3869 pub fn select_to_next_subword_end(
3870 &mut self,
3871 _: &SelectToNextSubwordEnd,
3872 cx: &mut ViewContext<Self>,
3873 ) {
3874 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3875 s.move_heads_with(|map, head, _| {
3876 (movement::next_subword_end(map, head), SelectionGoal::None)
3877 });
3878 })
3879 }
3880
3881 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
3882 self.transact(cx, |this, cx| {
3883 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3884 let line_mode = s.line_mode;
3885 s.move_with(|map, selection| {
3886 if selection.is_empty() && !line_mode {
3887 let cursor = movement::next_word_end(map, selection.head());
3888 selection.set_head(cursor, SelectionGoal::None);
3889 }
3890 });
3891 });
3892 this.insert("", cx);
3893 });
3894 }
3895
3896 pub fn delete_to_next_subword_end(
3897 &mut self,
3898 _: &DeleteToNextSubwordEnd,
3899 cx: &mut ViewContext<Self>,
3900 ) {
3901 self.transact(cx, |this, cx| {
3902 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3903 s.move_with(|map, selection| {
3904 if selection.is_empty() {
3905 let cursor = movement::next_subword_end(map, selection.head());
3906 selection.set_head(cursor, SelectionGoal::None);
3907 }
3908 });
3909 });
3910 this.insert("", cx);
3911 });
3912 }
3913
3914 pub fn move_to_beginning_of_line(
3915 &mut self,
3916 _: &MoveToBeginningOfLine,
3917 cx: &mut ViewContext<Self>,
3918 ) {
3919 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3920 s.move_cursors_with(|map, head, _| {
3921 (
3922 movement::line_beginning(map, head, true),
3923 SelectionGoal::None,
3924 )
3925 });
3926 })
3927 }
3928
3929 pub fn select_to_beginning_of_line(
3930 &mut self,
3931 action: &SelectToBeginningOfLine,
3932 cx: &mut ViewContext<Self>,
3933 ) {
3934 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3935 s.move_heads_with(|map, head, _| {
3936 (
3937 movement::line_beginning(map, head, action.stop_at_soft_wraps),
3938 SelectionGoal::None,
3939 )
3940 });
3941 });
3942 }
3943
3944 pub fn delete_to_beginning_of_line(
3945 &mut self,
3946 _: &DeleteToBeginningOfLine,
3947 cx: &mut ViewContext<Self>,
3948 ) {
3949 self.transact(cx, |this, cx| {
3950 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3951 s.move_with(|_, selection| {
3952 selection.reversed = true;
3953 });
3954 });
3955
3956 this.select_to_beginning_of_line(
3957 &SelectToBeginningOfLine {
3958 stop_at_soft_wraps: false,
3959 },
3960 cx,
3961 );
3962 this.backspace(&Backspace, cx);
3963 });
3964 }
3965
3966 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
3967 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3968 s.move_cursors_with(|map, head, _| {
3969 (movement::line_end(map, head, true), SelectionGoal::None)
3970 });
3971 })
3972 }
3973
3974 pub fn select_to_end_of_line(
3975 &mut self,
3976 action: &SelectToEndOfLine,
3977 cx: &mut ViewContext<Self>,
3978 ) {
3979 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3980 s.move_heads_with(|map, head, _| {
3981 (
3982 movement::line_end(map, head, action.stop_at_soft_wraps),
3983 SelectionGoal::None,
3984 )
3985 });
3986 })
3987 }
3988
3989 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
3990 self.transact(cx, |this, cx| {
3991 this.select_to_end_of_line(
3992 &SelectToEndOfLine {
3993 stop_at_soft_wraps: false,
3994 },
3995 cx,
3996 );
3997 this.delete(&Delete, cx);
3998 });
3999 }
4000
4001 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
4002 self.transact(cx, |this, cx| {
4003 this.select_to_end_of_line(
4004 &SelectToEndOfLine {
4005 stop_at_soft_wraps: false,
4006 },
4007 cx,
4008 );
4009 this.cut(&Cut, cx);
4010 });
4011 }
4012
4013 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
4014 if matches!(self.mode, EditorMode::SingleLine) {
4015 cx.propagate_action();
4016 return;
4017 }
4018
4019 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4020 s.select_ranges(vec![0..0]);
4021 });
4022 }
4023
4024 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
4025 let mut selection = self.selections.last::<Point>(cx);
4026 selection.set_head(Point::zero(), SelectionGoal::None);
4027
4028 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4029 s.select(vec![selection]);
4030 });
4031 }
4032
4033 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
4034 if matches!(self.mode, EditorMode::SingleLine) {
4035 cx.propagate_action();
4036 return;
4037 }
4038
4039 let cursor = self.buffer.read(cx).read(cx).len();
4040 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4041 s.select_ranges(vec![cursor..cursor])
4042 });
4043 }
4044
4045 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
4046 self.nav_history = nav_history;
4047 }
4048
4049 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
4050 self.nav_history.as_ref()
4051 }
4052
4053 fn push_to_nav_history(
4054 &self,
4055 position: Anchor,
4056 new_position: Option<Point>,
4057 cx: &mut ViewContext<Self>,
4058 ) {
4059 if let Some(nav_history) = &self.nav_history {
4060 let buffer = self.buffer.read(cx).read(cx);
4061 let point = position.to_point(&buffer);
4062 let scroll_top_row = self.scroll_top_anchor.to_point(&buffer).row;
4063 drop(buffer);
4064
4065 if let Some(new_position) = new_position {
4066 let row_delta = (new_position.row as i64 - point.row as i64).abs();
4067 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
4068 return;
4069 }
4070 }
4071
4072 nav_history.push(
4073 Some(NavigationData {
4074 cursor_anchor: position,
4075 cursor_position: point,
4076 scroll_position: self.scroll_position,
4077 scroll_top_anchor: self.scroll_top_anchor.clone(),
4078 scroll_top_row,
4079 }),
4080 cx,
4081 );
4082 }
4083 }
4084
4085 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
4086 let buffer = self.buffer.read(cx).snapshot(cx);
4087 let mut selection = self.selections.first::<usize>(cx);
4088 selection.set_head(buffer.len(), SelectionGoal::None);
4089 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4090 s.select(vec![selection]);
4091 });
4092 }
4093
4094 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
4095 let end = self.buffer.read(cx).read(cx).len();
4096 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4097 s.select_ranges(vec![0..end]);
4098 });
4099 }
4100
4101 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
4102 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4103 let mut selections = self.selections.all::<Point>(cx);
4104 let max_point = display_map.buffer_snapshot.max_point();
4105 for selection in &mut selections {
4106 let rows = selection.spanned_rows(true, &display_map);
4107 selection.start = Point::new(rows.start, 0);
4108 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
4109 selection.reversed = false;
4110 }
4111 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4112 s.select(selections);
4113 });
4114 }
4115
4116 pub fn split_selection_into_lines(
4117 &mut self,
4118 _: &SplitSelectionIntoLines,
4119 cx: &mut ViewContext<Self>,
4120 ) {
4121 let mut to_unfold = Vec::new();
4122 let mut new_selection_ranges = Vec::new();
4123 {
4124 let selections = self.selections.all::<Point>(cx);
4125 let buffer = self.buffer.read(cx).read(cx);
4126 for selection in selections {
4127 for row in selection.start.row..selection.end.row {
4128 let cursor = Point::new(row, buffer.line_len(row));
4129 new_selection_ranges.push(cursor..cursor);
4130 }
4131 new_selection_ranges.push(selection.end..selection.end);
4132 to_unfold.push(selection.start..selection.end);
4133 }
4134 }
4135 self.unfold_ranges(to_unfold, true, cx);
4136 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4137 s.select_ranges(new_selection_ranges);
4138 });
4139 }
4140
4141 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
4142 self.add_selection(true, cx);
4143 }
4144
4145 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
4146 self.add_selection(false, cx);
4147 }
4148
4149 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
4150 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4151 let mut selections = self.selections.all::<Point>(cx);
4152 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
4153 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
4154 let range = oldest_selection.display_range(&display_map).sorted();
4155 let columns = cmp::min(range.start.column(), range.end.column())
4156 ..cmp::max(range.start.column(), range.end.column());
4157
4158 selections.clear();
4159 let mut stack = Vec::new();
4160 for row in range.start.row()..=range.end.row() {
4161 if let Some(selection) = self.selections.build_columnar_selection(
4162 &display_map,
4163 row,
4164 &columns,
4165 oldest_selection.reversed,
4166 ) {
4167 stack.push(selection.id);
4168 selections.push(selection);
4169 }
4170 }
4171
4172 if above {
4173 stack.reverse();
4174 }
4175
4176 AddSelectionsState { above, stack }
4177 });
4178
4179 let last_added_selection = *state.stack.last().unwrap();
4180 let mut new_selections = Vec::new();
4181 if above == state.above {
4182 let end_row = if above {
4183 0
4184 } else {
4185 display_map.max_point().row()
4186 };
4187
4188 'outer: for selection in selections {
4189 if selection.id == last_added_selection {
4190 let range = selection.display_range(&display_map).sorted();
4191 debug_assert_eq!(range.start.row(), range.end.row());
4192 let mut row = range.start.row();
4193 let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
4194 {
4195 start..end
4196 } else {
4197 cmp::min(range.start.column(), range.end.column())
4198 ..cmp::max(range.start.column(), range.end.column())
4199 };
4200
4201 while row != end_row {
4202 if above {
4203 row -= 1;
4204 } else {
4205 row += 1;
4206 }
4207
4208 if let Some(new_selection) = self.selections.build_columnar_selection(
4209 &display_map,
4210 row,
4211 &columns,
4212 selection.reversed,
4213 ) {
4214 state.stack.push(new_selection.id);
4215 if above {
4216 new_selections.push(new_selection);
4217 new_selections.push(selection);
4218 } else {
4219 new_selections.push(selection);
4220 new_selections.push(new_selection);
4221 }
4222
4223 continue 'outer;
4224 }
4225 }
4226 }
4227
4228 new_selections.push(selection);
4229 }
4230 } else {
4231 new_selections = selections;
4232 new_selections.retain(|s| s.id != last_added_selection);
4233 state.stack.pop();
4234 }
4235
4236 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4237 s.select(new_selections);
4238 });
4239 if state.stack.len() > 1 {
4240 self.add_selections_state = Some(state);
4241 }
4242 }
4243
4244 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) {
4245 self.push_to_selection_history();
4246 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4247 let buffer = &display_map.buffer_snapshot;
4248 let mut selections = self.selections.all::<usize>(cx);
4249 if let Some(mut select_next_state) = self.select_next_state.take() {
4250 let query = &select_next_state.query;
4251 if !select_next_state.done {
4252 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
4253 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
4254 let mut next_selected_range = None;
4255
4256 let bytes_after_last_selection =
4257 buffer.bytes_in_range(last_selection.end..buffer.len());
4258 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
4259 let query_matches = query
4260 .stream_find_iter(bytes_after_last_selection)
4261 .map(|result| (last_selection.end, result))
4262 .chain(
4263 query
4264 .stream_find_iter(bytes_before_first_selection)
4265 .map(|result| (0, result)),
4266 );
4267 for (start_offset, query_match) in query_matches {
4268 let query_match = query_match.unwrap(); // can only fail due to I/O
4269 let offset_range =
4270 start_offset + query_match.start()..start_offset + query_match.end();
4271 let display_range = offset_range.start.to_display_point(&display_map)
4272 ..offset_range.end.to_display_point(&display_map);
4273
4274 if !select_next_state.wordwise
4275 || (!movement::is_inside_word(&display_map, display_range.start)
4276 && !movement::is_inside_word(&display_map, display_range.end))
4277 {
4278 next_selected_range = Some(offset_range);
4279 break;
4280 }
4281 }
4282
4283 if let Some(next_selected_range) = next_selected_range {
4284 self.unfold_ranges([next_selected_range.clone()], false, cx);
4285 self.change_selections(Some(Autoscroll::Newest), cx, |s| {
4286 if action.replace_newest {
4287 s.delete(s.newest_anchor().id);
4288 }
4289 s.insert_range(next_selected_range);
4290 });
4291 } else {
4292 select_next_state.done = true;
4293 }
4294 }
4295
4296 self.select_next_state = Some(select_next_state);
4297 } else if selections.len() == 1 {
4298 let selection = selections.last_mut().unwrap();
4299 if selection.start == selection.end {
4300 let word_range = movement::surrounding_word(
4301 &display_map,
4302 selection.start.to_display_point(&display_map),
4303 );
4304 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
4305 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
4306 selection.goal = SelectionGoal::None;
4307 selection.reversed = false;
4308
4309 let query = buffer
4310 .text_for_range(selection.start..selection.end)
4311 .collect::<String>();
4312 let select_state = SelectNextState {
4313 query: AhoCorasick::new_auto_configured(&[query]),
4314 wordwise: true,
4315 done: false,
4316 };
4317 self.unfold_ranges([selection.start..selection.end], false, cx);
4318 self.change_selections(Some(Autoscroll::Newest), cx, |s| {
4319 s.select(selections);
4320 });
4321 self.select_next_state = Some(select_state);
4322 } else {
4323 let query = buffer
4324 .text_for_range(selection.start..selection.end)
4325 .collect::<String>();
4326 self.select_next_state = Some(SelectNextState {
4327 query: AhoCorasick::new_auto_configured(&[query]),
4328 wordwise: false,
4329 done: false,
4330 });
4331 self.select_next(action, cx);
4332 }
4333 }
4334 }
4335
4336 pub fn toggle_comments(&mut self, _: &ToggleComments, cx: &mut ViewContext<Self>) {
4337 self.transact(cx, |this, cx| {
4338 let mut selections = this.selections.all::<Point>(cx);
4339 let mut all_selection_lines_are_comments = true;
4340 let mut edit_ranges = Vec::new();
4341 let mut last_toggled_row = None;
4342 this.buffer.update(cx, |buffer, cx| {
4343 // TODO: Handle selections that cross excerpts
4344 for selection in &mut selections {
4345 // Get the line comment prefix. Split its trailing whitespace into a separate string,
4346 // as that portion won't be used for detecting if a line is a comment.
4347 let full_comment_prefix: Arc<str> = if let Some(prefix) = buffer
4348 .language_at(selection.start, cx)
4349 .and_then(|l| l.line_comment_prefix())
4350 {
4351 prefix.into()
4352 } else {
4353 return;
4354 };
4355 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
4356 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
4357 edit_ranges.clear();
4358 let snapshot = buffer.snapshot(cx);
4359
4360 let end_row =
4361 if selection.end.row > selection.start.row && selection.end.column == 0 {
4362 selection.end.row
4363 } else {
4364 selection.end.row + 1
4365 };
4366
4367 for row in selection.start.row..end_row {
4368 // If multiple selections contain a given row, avoid processing that
4369 // row more than once.
4370 if last_toggled_row == Some(row) {
4371 continue;
4372 } else {
4373 last_toggled_row = Some(row);
4374 }
4375
4376 if snapshot.is_line_blank(row) {
4377 continue;
4378 }
4379
4380 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
4381 let mut line_bytes = snapshot
4382 .bytes_in_range(start..snapshot.max_point())
4383 .flatten()
4384 .copied();
4385
4386 // If this line currently begins with the line comment prefix, then record
4387 // the range containing the prefix.
4388 if all_selection_lines_are_comments
4389 && line_bytes
4390 .by_ref()
4391 .take(comment_prefix.len())
4392 .eq(comment_prefix.bytes())
4393 {
4394 // Include any whitespace that matches the comment prefix.
4395 let matching_whitespace_len = line_bytes
4396 .zip(comment_prefix_whitespace.bytes())
4397 .take_while(|(a, b)| a == b)
4398 .count()
4399 as u32;
4400 let end = Point::new(
4401 row,
4402 start.column
4403 + comment_prefix.len() as u32
4404 + matching_whitespace_len,
4405 );
4406 edit_ranges.push(start..end);
4407 }
4408 // If this line does not begin with the line comment prefix, then record
4409 // the position where the prefix should be inserted.
4410 else {
4411 all_selection_lines_are_comments = false;
4412 edit_ranges.push(start..start);
4413 }
4414 }
4415
4416 if !edit_ranges.is_empty() {
4417 if all_selection_lines_are_comments {
4418 let empty_str: Arc<str> = "".into();
4419 buffer.edit(
4420 edit_ranges
4421 .iter()
4422 .cloned()
4423 .map(|range| (range, empty_str.clone())),
4424 cx,
4425 );
4426 } else {
4427 let min_column =
4428 edit_ranges.iter().map(|r| r.start.column).min().unwrap();
4429 let edits = edit_ranges.iter().map(|range| {
4430 let position = Point::new(range.start.row, min_column);
4431 (position..position, full_comment_prefix.clone())
4432 });
4433 buffer.edit(edits, cx);
4434 }
4435 }
4436 }
4437 });
4438
4439 let selections = this.selections.all::<usize>(cx);
4440 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
4441 });
4442 }
4443
4444 pub fn select_larger_syntax_node(
4445 &mut self,
4446 _: &SelectLargerSyntaxNode,
4447 cx: &mut ViewContext<Self>,
4448 ) {
4449 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4450 let buffer = self.buffer.read(cx).snapshot(cx);
4451 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
4452
4453 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
4454 let mut selected_larger_node = false;
4455 let new_selections = old_selections
4456 .iter()
4457 .map(|selection| {
4458 let old_range = selection.start..selection.end;
4459 let mut new_range = old_range.clone();
4460 while let Some(containing_range) =
4461 buffer.range_for_syntax_ancestor(new_range.clone())
4462 {
4463 new_range = containing_range;
4464 if !display_map.intersects_fold(new_range.start)
4465 && !display_map.intersects_fold(new_range.end)
4466 {
4467 break;
4468 }
4469 }
4470
4471 selected_larger_node |= new_range != old_range;
4472 Selection {
4473 id: selection.id,
4474 start: new_range.start,
4475 end: new_range.end,
4476 goal: SelectionGoal::None,
4477 reversed: selection.reversed,
4478 }
4479 })
4480 .collect::<Vec<_>>();
4481
4482 if selected_larger_node {
4483 stack.push(old_selections);
4484 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4485 s.select(new_selections);
4486 });
4487 }
4488 self.select_larger_syntax_node_stack = stack;
4489 }
4490
4491 pub fn select_smaller_syntax_node(
4492 &mut self,
4493 _: &SelectSmallerSyntaxNode,
4494 cx: &mut ViewContext<Self>,
4495 ) {
4496 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
4497 if let Some(selections) = stack.pop() {
4498 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4499 s.select(selections.to_vec());
4500 });
4501 }
4502 self.select_larger_syntax_node_stack = stack;
4503 }
4504
4505 pub fn move_to_enclosing_bracket(
4506 &mut self,
4507 _: &MoveToEnclosingBracket,
4508 cx: &mut ViewContext<Self>,
4509 ) {
4510 let buffer = self.buffer.read(cx).snapshot(cx);
4511 let mut selections = self.selections.all::<usize>(cx);
4512 for selection in &mut selections {
4513 if let Some((open_range, close_range)) =
4514 buffer.enclosing_bracket_ranges(selection.start..selection.end)
4515 {
4516 let close_range = close_range.to_inclusive();
4517 let destination = if close_range.contains(&selection.start)
4518 && close_range.contains(&selection.end)
4519 {
4520 open_range.end
4521 } else {
4522 *close_range.start()
4523 };
4524 selection.start = destination;
4525 selection.end = destination;
4526 }
4527 }
4528
4529 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4530 s.select(selections);
4531 });
4532 }
4533
4534 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
4535 self.end_selection(cx);
4536 self.selection_history.mode = SelectionHistoryMode::Undoing;
4537 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
4538 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
4539 self.select_next_state = entry.select_next_state;
4540 self.add_selections_state = entry.add_selections_state;
4541 self.request_autoscroll(Autoscroll::Newest, cx);
4542 }
4543 self.selection_history.mode = SelectionHistoryMode::Normal;
4544 }
4545
4546 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
4547 self.end_selection(cx);
4548 self.selection_history.mode = SelectionHistoryMode::Redoing;
4549 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
4550 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
4551 self.select_next_state = entry.select_next_state;
4552 self.add_selections_state = entry.add_selections_state;
4553 self.request_autoscroll(Autoscroll::Newest, cx);
4554 }
4555 self.selection_history.mode = SelectionHistoryMode::Normal;
4556 }
4557
4558 fn go_to_next_diagnostic(&mut self, _: &GoToNextDiagnostic, cx: &mut ViewContext<Self>) {
4559 self.go_to_diagnostic(Direction::Next, cx)
4560 }
4561
4562 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
4563 self.go_to_diagnostic(Direction::Prev, cx)
4564 }
4565
4566 pub fn go_to_diagnostic(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
4567 let buffer = self.buffer.read(cx).snapshot(cx);
4568 let selection = self.selections.newest::<usize>(cx);
4569 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
4570 active_diagnostics
4571 .primary_range
4572 .to_offset(&buffer)
4573 .to_inclusive()
4574 });
4575 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
4576 if active_primary_range.contains(&selection.head()) {
4577 *active_primary_range.end()
4578 } else {
4579 selection.head()
4580 }
4581 } else {
4582 selection.head()
4583 };
4584
4585 loop {
4586 let mut diagnostics = if direction == Direction::Prev {
4587 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
4588 } else {
4589 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
4590 };
4591 let group = diagnostics.find_map(|entry| {
4592 if entry.diagnostic.is_primary
4593 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
4594 && !entry.range.is_empty()
4595 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
4596 {
4597 Some((entry.range, entry.diagnostic.group_id))
4598 } else {
4599 None
4600 }
4601 });
4602
4603 if let Some((primary_range, group_id)) = group {
4604 self.activate_diagnostics(group_id, cx);
4605 self.change_selections(Some(Autoscroll::Center), cx, |s| {
4606 s.select(vec![Selection {
4607 id: selection.id,
4608 start: primary_range.start,
4609 end: primary_range.start,
4610 reversed: false,
4611 goal: SelectionGoal::None,
4612 }]);
4613 });
4614 break;
4615 } else {
4616 // Cycle around to the start of the buffer, potentially moving back to the start of
4617 // the currently active diagnostic.
4618 active_primary_range.take();
4619 if direction == Direction::Prev {
4620 if search_start == buffer.len() {
4621 break;
4622 } else {
4623 search_start = buffer.len();
4624 }
4625 } else {
4626 if search_start == 0 {
4627 break;
4628 } else {
4629 search_start = 0;
4630 }
4631 }
4632 }
4633 }
4634 }
4635
4636 pub fn go_to_definition(
4637 workspace: &mut Workspace,
4638 _: &GoToDefinition,
4639 cx: &mut ViewContext<Workspace>,
4640 ) {
4641 let active_item = workspace.active_item(cx);
4642 let editor_handle = if let Some(editor) = active_item
4643 .as_ref()
4644 .and_then(|item| item.act_as::<Self>(cx))
4645 {
4646 editor
4647 } else {
4648 return;
4649 };
4650
4651 let editor = editor_handle.read(cx);
4652 let buffer = editor.buffer.read(cx);
4653 let head = editor.selections.newest::<usize>(cx).head();
4654 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
4655 text_anchor
4656 } else {
4657 return;
4658 };
4659
4660 let project = workspace.project().clone();
4661 let definitions = project.update(cx, |project, cx| project.definition(&buffer, head, cx));
4662 cx.spawn(|workspace, mut cx| async move {
4663 let definitions = definitions.await?;
4664 workspace.update(&mut cx, |workspace, cx| {
4665 Editor::navigate_to_definitions(workspace, editor_handle, definitions, cx);
4666 });
4667
4668 Ok::<(), anyhow::Error>(())
4669 })
4670 .detach_and_log_err(cx);
4671 }
4672
4673 pub fn navigate_to_definitions(
4674 workspace: &mut Workspace,
4675 editor_handle: ViewHandle<Editor>,
4676 definitions: Vec<LocationLink>,
4677 cx: &mut ViewContext<Workspace>,
4678 ) {
4679 let pane = workspace.active_pane().clone();
4680 for definition in definitions {
4681 let range = definition
4682 .target
4683 .range
4684 .to_offset(definition.target.buffer.read(cx));
4685
4686 let target_editor_handle = workspace.open_project_item(definition.target.buffer, cx);
4687 target_editor_handle.update(cx, |target_editor, cx| {
4688 // When selecting a definition in a different buffer, disable the nav history
4689 // to avoid creating a history entry at the previous cursor location.
4690 if editor_handle != target_editor_handle {
4691 pane.update(cx, |pane, _| pane.disable_history());
4692 }
4693 target_editor.change_selections(Some(Autoscroll::Center), cx, |s| {
4694 s.select_ranges([range]);
4695 });
4696
4697 pane.update(cx, |pane, _| pane.enable_history());
4698 });
4699 }
4700 }
4701
4702 pub fn find_all_references(
4703 workspace: &mut Workspace,
4704 _: &FindAllReferences,
4705 cx: &mut ViewContext<Workspace>,
4706 ) -> Option<Task<Result<()>>> {
4707 let active_item = workspace.active_item(cx)?;
4708 let editor_handle = active_item.act_as::<Self>(cx)?;
4709
4710 let editor = editor_handle.read(cx);
4711 let buffer = editor.buffer.read(cx);
4712 let head = editor.selections.newest::<usize>(cx).head();
4713 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
4714 let replica_id = editor.replica_id(cx);
4715
4716 let project = workspace.project().clone();
4717 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
4718 Some(cx.spawn(|workspace, mut cx| async move {
4719 let mut locations = references.await?;
4720 if locations.is_empty() {
4721 return Ok(());
4722 }
4723
4724 locations.sort_by_key(|location| location.buffer.id());
4725 let mut locations = locations.into_iter().peekable();
4726 let mut ranges_to_highlight = Vec::new();
4727
4728 let excerpt_buffer = cx.add_model(|cx| {
4729 let mut symbol_name = None;
4730 let mut multibuffer = MultiBuffer::new(replica_id);
4731 while let Some(location) = locations.next() {
4732 let buffer = location.buffer.read(cx);
4733 let mut ranges_for_buffer = Vec::new();
4734 let range = location.range.to_offset(buffer);
4735 ranges_for_buffer.push(range.clone());
4736 if symbol_name.is_none() {
4737 symbol_name = Some(buffer.text_for_range(range).collect::<String>());
4738 }
4739
4740 while let Some(next_location) = locations.peek() {
4741 if next_location.buffer == location.buffer {
4742 ranges_for_buffer.push(next_location.range.to_offset(buffer));
4743 locations.next();
4744 } else {
4745 break;
4746 }
4747 }
4748
4749 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
4750 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
4751 location.buffer.clone(),
4752 ranges_for_buffer,
4753 1,
4754 cx,
4755 ));
4756 }
4757 multibuffer.with_title(format!("References to `{}`", symbol_name.unwrap()))
4758 });
4759
4760 workspace.update(&mut cx, |workspace, cx| {
4761 let editor =
4762 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
4763 editor.update(cx, |editor, cx| {
4764 editor.highlight_background::<Self>(
4765 ranges_to_highlight,
4766 |theme| theme.editor.highlighted_line_background,
4767 cx,
4768 );
4769 });
4770 workspace.add_item(Box::new(editor), cx);
4771 });
4772
4773 Ok(())
4774 }))
4775 }
4776
4777 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
4778 use language::ToOffset as _;
4779
4780 let project = self.project.clone()?;
4781 let selection = self.selections.newest_anchor().clone();
4782 let (cursor_buffer, cursor_buffer_position) = self
4783 .buffer
4784 .read(cx)
4785 .text_anchor_for_position(selection.head(), cx)?;
4786 let (tail_buffer, _) = self
4787 .buffer
4788 .read(cx)
4789 .text_anchor_for_position(selection.tail(), cx)?;
4790 if tail_buffer != cursor_buffer {
4791 return None;
4792 }
4793
4794 let snapshot = cursor_buffer.read(cx).snapshot();
4795 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
4796 let prepare_rename = project.update(cx, |project, cx| {
4797 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
4798 });
4799
4800 Some(cx.spawn(|this, mut cx| async move {
4801 let rename_range = if let Some(range) = prepare_rename.await? {
4802 Some(range)
4803 } else {
4804 this.read_with(&cx, |this, cx| {
4805 let buffer = this.buffer.read(cx).snapshot(cx);
4806 let mut buffer_highlights = this
4807 .document_highlights_for_position(selection.head(), &buffer)
4808 .filter(|highlight| {
4809 highlight.start.excerpt_id() == selection.head().excerpt_id()
4810 && highlight.end.excerpt_id() == selection.head().excerpt_id()
4811 });
4812 buffer_highlights
4813 .next()
4814 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
4815 })
4816 };
4817 if let Some(rename_range) = rename_range {
4818 let rename_buffer_range = rename_range.to_offset(&snapshot);
4819 let cursor_offset_in_rename_range =
4820 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
4821
4822 this.update(&mut cx, |this, cx| {
4823 this.take_rename(false, cx);
4824 let style = this.style(cx);
4825 let buffer = this.buffer.read(cx).read(cx);
4826 let cursor_offset = selection.head().to_offset(&buffer);
4827 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
4828 let rename_end = rename_start + rename_buffer_range.len();
4829 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
4830 let mut old_highlight_id = None;
4831 let old_name: Arc<str> = buffer
4832 .chunks(rename_start..rename_end, true)
4833 .map(|chunk| {
4834 if old_highlight_id.is_none() {
4835 old_highlight_id = chunk.syntax_highlight_id;
4836 }
4837 chunk.text
4838 })
4839 .collect::<String>()
4840 .into();
4841
4842 drop(buffer);
4843
4844 // Position the selection in the rename editor so that it matches the current selection.
4845 this.show_local_selections = false;
4846 let rename_editor = cx.add_view(|cx| {
4847 let mut editor = Editor::single_line(None, cx);
4848 if let Some(old_highlight_id) = old_highlight_id {
4849 editor.override_text_style =
4850 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
4851 }
4852 editor
4853 .buffer
4854 .update(cx, |buffer, cx| buffer.edit([(0..0, old_name.clone())], cx));
4855 editor.select_all(&SelectAll, cx);
4856 editor
4857 });
4858
4859 let ranges = this
4860 .clear_background_highlights::<DocumentHighlightWrite>(cx)
4861 .into_iter()
4862 .flat_map(|(_, ranges)| ranges)
4863 .chain(
4864 this.clear_background_highlights::<DocumentHighlightRead>(cx)
4865 .into_iter()
4866 .flat_map(|(_, ranges)| ranges),
4867 )
4868 .collect();
4869
4870 this.highlight_text::<Rename>(
4871 ranges,
4872 HighlightStyle {
4873 fade_out: Some(style.rename_fade),
4874 ..Default::default()
4875 },
4876 cx,
4877 );
4878 cx.focus(&rename_editor);
4879 let block_id = this.insert_blocks(
4880 [BlockProperties {
4881 style: BlockStyle::Flex,
4882 position: range.start.clone(),
4883 height: 1,
4884 render: Arc::new({
4885 let editor = rename_editor.clone();
4886 move |cx: &mut BlockContext| {
4887 ChildView::new(editor.clone())
4888 .contained()
4889 .with_padding_left(cx.anchor_x)
4890 .boxed()
4891 }
4892 }),
4893 disposition: BlockDisposition::Below,
4894 }],
4895 cx,
4896 )[0];
4897 this.pending_rename = Some(RenameState {
4898 range,
4899 old_name,
4900 editor: rename_editor,
4901 block_id,
4902 });
4903 });
4904 }
4905
4906 Ok(())
4907 }))
4908 }
4909
4910 pub fn confirm_rename(
4911 workspace: &mut Workspace,
4912 _: &ConfirmRename,
4913 cx: &mut ViewContext<Workspace>,
4914 ) -> Option<Task<Result<()>>> {
4915 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
4916
4917 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
4918 let rename = editor.take_rename(false, cx)?;
4919 let buffer = editor.buffer.read(cx);
4920 let (start_buffer, start) =
4921 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
4922 let (end_buffer, end) =
4923 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
4924 if start_buffer == end_buffer {
4925 let new_name = rename.editor.read(cx).text(cx);
4926 Some((start_buffer, start..end, rename.old_name, new_name))
4927 } else {
4928 None
4929 }
4930 })?;
4931
4932 let rename = workspace.project().clone().update(cx, |project, cx| {
4933 project.perform_rename(
4934 buffer.clone(),
4935 range.start.clone(),
4936 new_name.clone(),
4937 true,
4938 cx,
4939 )
4940 });
4941
4942 Some(cx.spawn(|workspace, mut cx| async move {
4943 let project_transaction = rename.await?;
4944 Self::open_project_transaction(
4945 editor.clone(),
4946 workspace,
4947 project_transaction,
4948 format!("Rename: {} → {}", old_name, new_name),
4949 cx.clone(),
4950 )
4951 .await?;
4952
4953 editor.update(&mut cx, |editor, cx| {
4954 editor.refresh_document_highlights(cx);
4955 });
4956 Ok(())
4957 }))
4958 }
4959
4960 fn take_rename(
4961 &mut self,
4962 moving_cursor: bool,
4963 cx: &mut ViewContext<Self>,
4964 ) -> Option<RenameState> {
4965 let rename = self.pending_rename.take()?;
4966 self.remove_blocks([rename.block_id].into_iter().collect(), cx);
4967 self.clear_text_highlights::<Rename>(cx);
4968 self.show_local_selections = true;
4969
4970 if moving_cursor {
4971 let rename_editor = rename.editor.read(cx);
4972 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
4973
4974 // Update the selection to match the position of the selection inside
4975 // the rename editor.
4976 let snapshot = self.buffer.read(cx).read(cx);
4977 let rename_range = rename.range.to_offset(&snapshot);
4978 let cursor_in_editor = snapshot
4979 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
4980 .min(rename_range.end);
4981 drop(snapshot);
4982
4983 self.change_selections(None, cx, |s| {
4984 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
4985 });
4986 } else {
4987 self.refresh_document_highlights(cx);
4988 }
4989
4990 Some(rename)
4991 }
4992
4993 #[cfg(any(test, feature = "test-support"))]
4994 pub fn pending_rename(&self) -> Option<&RenameState> {
4995 self.pending_rename.as_ref()
4996 }
4997
4998 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
4999 if let Some(project) = self.project.clone() {
5000 self.buffer.update(cx, |multi_buffer, cx| {
5001 project.update(cx, |project, cx| {
5002 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
5003 });
5004 })
5005 }
5006 }
5007
5008 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
5009 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
5010 let buffer = self.buffer.read(cx).snapshot(cx);
5011 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
5012 let is_valid = buffer
5013 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
5014 .any(|entry| {
5015 entry.diagnostic.is_primary
5016 && !entry.range.is_empty()
5017 && entry.range.start == primary_range_start
5018 && entry.diagnostic.message == active_diagnostics.primary_message
5019 });
5020
5021 if is_valid != active_diagnostics.is_valid {
5022 active_diagnostics.is_valid = is_valid;
5023 let mut new_styles = HashMap::default();
5024 for (block_id, diagnostic) in &active_diagnostics.blocks {
5025 new_styles.insert(
5026 *block_id,
5027 diagnostic_block_renderer(diagnostic.clone(), is_valid),
5028 );
5029 }
5030 self.display_map
5031 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
5032 }
5033 }
5034 }
5035
5036 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) {
5037 self.dismiss_diagnostics(cx);
5038 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
5039 let buffer = self.buffer.read(cx).snapshot(cx);
5040
5041 let mut primary_range = None;
5042 let mut primary_message = None;
5043 let mut group_end = Point::zero();
5044 let diagnostic_group = buffer
5045 .diagnostic_group::<Point>(group_id)
5046 .map(|entry| {
5047 if entry.range.end > group_end {
5048 group_end = entry.range.end;
5049 }
5050 if entry.diagnostic.is_primary {
5051 primary_range = Some(entry.range.clone());
5052 primary_message = Some(entry.diagnostic.message.clone());
5053 }
5054 entry
5055 })
5056 .collect::<Vec<_>>();
5057 let primary_range = primary_range.unwrap();
5058 let primary_message = primary_message.unwrap();
5059 let primary_range =
5060 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
5061
5062 let blocks = display_map
5063 .insert_blocks(
5064 diagnostic_group.iter().map(|entry| {
5065 let diagnostic = entry.diagnostic.clone();
5066 let message_height = diagnostic.message.lines().count() as u8;
5067 BlockProperties {
5068 style: BlockStyle::Fixed,
5069 position: buffer.anchor_after(entry.range.start),
5070 height: message_height,
5071 render: diagnostic_block_renderer(diagnostic, true),
5072 disposition: BlockDisposition::Below,
5073 }
5074 }),
5075 cx,
5076 )
5077 .into_iter()
5078 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
5079 .collect();
5080
5081 Some(ActiveDiagnosticGroup {
5082 primary_range,
5083 primary_message,
5084 blocks,
5085 is_valid: true,
5086 })
5087 });
5088 }
5089
5090 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
5091 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
5092 self.display_map.update(cx, |display_map, cx| {
5093 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
5094 });
5095 cx.notify();
5096 }
5097 }
5098
5099 pub fn set_selections_from_remote(
5100 &mut self,
5101 selections: Vec<Selection<Anchor>>,
5102 cx: &mut ViewContext<Self>,
5103 ) {
5104 let old_cursor_position = self.selections.newest_anchor().head();
5105 self.selections.change_with(cx, |s| {
5106 s.select_anchors(selections);
5107 });
5108 self.selections_did_change(false, &old_cursor_position, cx);
5109 }
5110
5111 fn push_to_selection_history(&mut self) {
5112 self.selection_history.push(SelectionHistoryEntry {
5113 selections: self.selections.disjoint_anchors().clone(),
5114 select_next_state: self.select_next_state.clone(),
5115 add_selections_state: self.add_selections_state.clone(),
5116 });
5117 }
5118
5119 pub fn request_autoscroll(&mut self, autoscroll: Autoscroll, cx: &mut ViewContext<Self>) {
5120 self.autoscroll_request = Some((autoscroll, true));
5121 cx.notify();
5122 }
5123
5124 fn request_autoscroll_remotely(&mut self, autoscroll: Autoscroll, cx: &mut ViewContext<Self>) {
5125 self.autoscroll_request = Some((autoscroll, false));
5126 cx.notify();
5127 }
5128
5129 pub fn transact(
5130 &mut self,
5131 cx: &mut ViewContext<Self>,
5132 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
5133 ) {
5134 self.start_transaction_at(Instant::now(), cx);
5135 update(self, cx);
5136 self.end_transaction_at(Instant::now(), cx);
5137 }
5138
5139 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
5140 self.end_selection(cx);
5141 if let Some(tx_id) = self
5142 .buffer
5143 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
5144 {
5145 self.selection_history
5146 .insert_transaction(tx_id, self.selections.disjoint_anchors().clone());
5147 }
5148 }
5149
5150 fn end_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
5151 if let Some(tx_id) = self
5152 .buffer
5153 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
5154 {
5155 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
5156 *end_selections = Some(self.selections.disjoint_anchors().clone());
5157 } else {
5158 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
5159 }
5160
5161 cx.emit(Event::Edited);
5162 }
5163 }
5164
5165 pub fn page_up(&mut self, _: &PageUp, _: &mut ViewContext<Self>) {
5166 log::info!("Editor::page_up");
5167 }
5168
5169 pub fn page_down(&mut self, _: &PageDown, _: &mut ViewContext<Self>) {
5170 log::info!("Editor::page_down");
5171 }
5172
5173 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
5174 let mut fold_ranges = Vec::new();
5175
5176 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5177 let selections = self.selections.all::<Point>(cx);
5178 for selection in selections {
5179 let range = selection.display_range(&display_map).sorted();
5180 let buffer_start_row = range.start.to_point(&display_map).row;
5181
5182 for row in (0..=range.end.row()).rev() {
5183 if self.is_line_foldable(&display_map, row) && !display_map.is_line_folded(row) {
5184 let fold_range = self.foldable_range_for_line(&display_map, row);
5185 if fold_range.end.row >= buffer_start_row {
5186 fold_ranges.push(fold_range);
5187 if row <= range.start.row() {
5188 break;
5189 }
5190 }
5191 }
5192 }
5193 }
5194
5195 self.fold_ranges(fold_ranges, cx);
5196 }
5197
5198 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
5199 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5200 let buffer = &display_map.buffer_snapshot;
5201 let selections = self.selections.all::<Point>(cx);
5202 let ranges = selections
5203 .iter()
5204 .map(|s| {
5205 let range = s.display_range(&display_map).sorted();
5206 let mut start = range.start.to_point(&display_map);
5207 let mut end = range.end.to_point(&display_map);
5208 start.column = 0;
5209 end.column = buffer.line_len(end.row);
5210 start..end
5211 })
5212 .collect::<Vec<_>>();
5213 self.unfold_ranges(ranges, true, cx);
5214 }
5215
5216 fn is_line_foldable(&self, display_map: &DisplaySnapshot, display_row: u32) -> bool {
5217 let max_point = display_map.max_point();
5218 if display_row >= max_point.row() {
5219 false
5220 } else {
5221 let (start_indent, is_blank) = display_map.line_indent(display_row);
5222 if is_blank {
5223 false
5224 } else {
5225 for display_row in display_row + 1..=max_point.row() {
5226 let (indent, is_blank) = display_map.line_indent(display_row);
5227 if !is_blank {
5228 return indent > start_indent;
5229 }
5230 }
5231 false
5232 }
5233 }
5234 }
5235
5236 fn foldable_range_for_line(
5237 &self,
5238 display_map: &DisplaySnapshot,
5239 start_row: u32,
5240 ) -> Range<Point> {
5241 let max_point = display_map.max_point();
5242
5243 let (start_indent, _) = display_map.line_indent(start_row);
5244 let start = DisplayPoint::new(start_row, display_map.line_len(start_row));
5245 let mut end = None;
5246 for row in start_row + 1..=max_point.row() {
5247 let (indent, is_blank) = display_map.line_indent(row);
5248 if !is_blank && indent <= start_indent {
5249 end = Some(DisplayPoint::new(row - 1, display_map.line_len(row - 1)));
5250 break;
5251 }
5252 }
5253
5254 let end = end.unwrap_or(max_point);
5255 return start.to_point(display_map)..end.to_point(display_map);
5256 }
5257
5258 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
5259 let selections = self.selections.all::<Point>(cx);
5260 let ranges = selections.into_iter().map(|s| s.start..s.end);
5261 self.fold_ranges(ranges, cx);
5262 }
5263
5264 pub fn fold_ranges<T: ToOffset>(
5265 &mut self,
5266 ranges: impl IntoIterator<Item = Range<T>>,
5267 cx: &mut ViewContext<Self>,
5268 ) {
5269 let mut ranges = ranges.into_iter().peekable();
5270 if ranges.peek().is_some() {
5271 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
5272 self.request_autoscroll(Autoscroll::Fit, cx);
5273 cx.notify();
5274 }
5275 }
5276
5277 pub fn unfold_ranges<T: ToOffset>(
5278 &mut self,
5279 ranges: impl IntoIterator<Item = Range<T>>,
5280 inclusive: bool,
5281 cx: &mut ViewContext<Self>,
5282 ) {
5283 let mut ranges = ranges.into_iter().peekable();
5284 if ranges.peek().is_some() {
5285 self.display_map
5286 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
5287 self.request_autoscroll(Autoscroll::Fit, cx);
5288 cx.notify();
5289 }
5290 }
5291
5292 pub fn insert_blocks(
5293 &mut self,
5294 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
5295 cx: &mut ViewContext<Self>,
5296 ) -> Vec<BlockId> {
5297 let blocks = self
5298 .display_map
5299 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
5300 self.request_autoscroll(Autoscroll::Fit, cx);
5301 blocks
5302 }
5303
5304 pub fn replace_blocks(
5305 &mut self,
5306 blocks: HashMap<BlockId, RenderBlock>,
5307 cx: &mut ViewContext<Self>,
5308 ) {
5309 self.display_map
5310 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
5311 self.request_autoscroll(Autoscroll::Fit, cx);
5312 }
5313
5314 pub fn remove_blocks(&mut self, block_ids: HashSet<BlockId>, cx: &mut ViewContext<Self>) {
5315 self.display_map.update(cx, |display_map, cx| {
5316 display_map.remove_blocks(block_ids, cx)
5317 });
5318 }
5319
5320 pub fn longest_row(&self, cx: &mut MutableAppContext) -> u32 {
5321 self.display_map
5322 .update(cx, |map, cx| map.snapshot(cx))
5323 .longest_row()
5324 }
5325
5326 pub fn max_point(&self, cx: &mut MutableAppContext) -> DisplayPoint {
5327 self.display_map
5328 .update(cx, |map, cx| map.snapshot(cx))
5329 .max_point()
5330 }
5331
5332 pub fn text(&self, cx: &AppContext) -> String {
5333 self.buffer.read(cx).read(cx).text()
5334 }
5335
5336 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
5337 self.transact(cx, |this, cx| {
5338 this.buffer
5339 .read(cx)
5340 .as_singleton()
5341 .expect("you can only call set_text on editors for singleton buffers")
5342 .update(cx, |buffer, cx| buffer.set_text(text, cx));
5343 });
5344 }
5345
5346 pub fn display_text(&self, cx: &mut MutableAppContext) -> String {
5347 self.display_map
5348 .update(cx, |map, cx| map.snapshot(cx))
5349 .text()
5350 }
5351
5352 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
5353 let language_name = self
5354 .buffer
5355 .read(cx)
5356 .as_singleton()
5357 .and_then(|singleton_buffer| singleton_buffer.read(cx).language())
5358 .map(|l| l.name());
5359
5360 let settings = cx.global::<Settings>();
5361 let mode = self
5362 .soft_wrap_mode_override
5363 .unwrap_or_else(|| settings.soft_wrap(language_name.as_deref()));
5364 match mode {
5365 settings::SoftWrap::None => SoftWrap::None,
5366 settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
5367 settings::SoftWrap::PreferredLineLength => {
5368 SoftWrap::Column(settings.preferred_line_length(language_name.as_deref()))
5369 }
5370 }
5371 }
5372
5373 pub fn set_soft_wrap_mode(&mut self, mode: settings::SoftWrap, cx: &mut ViewContext<Self>) {
5374 self.soft_wrap_mode_override = Some(mode);
5375 cx.notify();
5376 }
5377
5378 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut MutableAppContext) -> bool {
5379 self.display_map
5380 .update(cx, |map, cx| map.set_wrap_width(width, cx))
5381 }
5382
5383 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
5384 self.highlighted_rows = rows;
5385 }
5386
5387 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
5388 self.highlighted_rows.clone()
5389 }
5390
5391 pub fn highlight_background<T: 'static>(
5392 &mut self,
5393 ranges: Vec<Range<Anchor>>,
5394 color_fetcher: fn(&Theme) -> Color,
5395 cx: &mut ViewContext<Self>,
5396 ) {
5397 self.background_highlights
5398 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
5399 cx.notify();
5400 }
5401
5402 pub fn clear_background_highlights<T: 'static>(
5403 &mut self,
5404 cx: &mut ViewContext<Self>,
5405 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
5406 cx.notify();
5407 self.background_highlights.remove(&TypeId::of::<T>())
5408 }
5409
5410 #[cfg(feature = "test-support")]
5411 pub fn all_background_highlights(
5412 &mut self,
5413 cx: &mut ViewContext<Self>,
5414 ) -> Vec<(Range<DisplayPoint>, Color)> {
5415 let snapshot = self.snapshot(cx);
5416 let buffer = &snapshot.buffer_snapshot;
5417 let start = buffer.anchor_before(0);
5418 let end = buffer.anchor_after(buffer.len());
5419 let theme = cx.global::<Settings>().theme.as_ref();
5420 self.background_highlights_in_range(start..end, &snapshot, theme)
5421 }
5422
5423 fn document_highlights_for_position<'a>(
5424 &'a self,
5425 position: Anchor,
5426 buffer: &'a MultiBufferSnapshot,
5427 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
5428 let read_highlights = self
5429 .background_highlights
5430 .get(&TypeId::of::<DocumentHighlightRead>())
5431 .map(|h| &h.1);
5432 let write_highlights = self
5433 .background_highlights
5434 .get(&TypeId::of::<DocumentHighlightWrite>())
5435 .map(|h| &h.1);
5436 let left_position = position.bias_left(buffer);
5437 let right_position = position.bias_right(buffer);
5438 read_highlights
5439 .into_iter()
5440 .chain(write_highlights)
5441 .flat_map(move |ranges| {
5442 let start_ix = match ranges.binary_search_by(|probe| {
5443 let cmp = probe.end.cmp(&left_position, &buffer);
5444 if cmp.is_ge() {
5445 Ordering::Greater
5446 } else {
5447 Ordering::Less
5448 }
5449 }) {
5450 Ok(i) | Err(i) => i,
5451 };
5452
5453 let right_position = right_position.clone();
5454 ranges[start_ix..]
5455 .iter()
5456 .take_while(move |range| range.start.cmp(&right_position, &buffer).is_le())
5457 })
5458 }
5459
5460 pub fn background_highlights_in_range(
5461 &self,
5462 search_range: Range<Anchor>,
5463 display_snapshot: &DisplaySnapshot,
5464 theme: &Theme,
5465 ) -> Vec<(Range<DisplayPoint>, Color)> {
5466 let mut results = Vec::new();
5467 let buffer = &display_snapshot.buffer_snapshot;
5468 for (color_fetcher, ranges) in self.background_highlights.values() {
5469 let color = color_fetcher(theme);
5470 let start_ix = match ranges.binary_search_by(|probe| {
5471 let cmp = probe.end.cmp(&search_range.start, &buffer);
5472 if cmp.is_gt() {
5473 Ordering::Greater
5474 } else {
5475 Ordering::Less
5476 }
5477 }) {
5478 Ok(i) | Err(i) => i,
5479 };
5480 for range in &ranges[start_ix..] {
5481 if range.start.cmp(&search_range.end, &buffer).is_ge() {
5482 break;
5483 }
5484 let start = range
5485 .start
5486 .to_point(buffer)
5487 .to_display_point(display_snapshot);
5488 let end = range
5489 .end
5490 .to_point(buffer)
5491 .to_display_point(display_snapshot);
5492 results.push((start..end, color))
5493 }
5494 }
5495 results
5496 }
5497
5498 pub fn highlight_text<T: 'static>(
5499 &mut self,
5500 ranges: Vec<Range<Anchor>>,
5501 style: HighlightStyle,
5502 cx: &mut ViewContext<Self>,
5503 ) {
5504 self.display_map.update(cx, |map, _| {
5505 map.highlight_text(TypeId::of::<T>(), ranges, style)
5506 });
5507 cx.notify();
5508 }
5509
5510 pub fn clear_text_highlights<T: 'static>(
5511 &mut self,
5512 cx: &mut ViewContext<Self>,
5513 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
5514 cx.notify();
5515 self.display_map
5516 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()))
5517 }
5518
5519 fn next_blink_epoch(&mut self) -> usize {
5520 self.blink_epoch += 1;
5521 self.blink_epoch
5522 }
5523
5524 fn pause_cursor_blinking(&mut self, cx: &mut ViewContext<Self>) {
5525 if !self.focused {
5526 return;
5527 }
5528
5529 self.show_local_cursors = true;
5530 cx.notify();
5531
5532 let epoch = self.next_blink_epoch();
5533 cx.spawn(|this, mut cx| {
5534 let this = this.downgrade();
5535 async move {
5536 Timer::after(CURSOR_BLINK_INTERVAL).await;
5537 if let Some(this) = this.upgrade(&cx) {
5538 this.update(&mut cx, |this, cx| this.resume_cursor_blinking(epoch, cx))
5539 }
5540 }
5541 })
5542 .detach();
5543 }
5544
5545 fn resume_cursor_blinking(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
5546 if epoch == self.blink_epoch {
5547 self.blinking_paused = false;
5548 self.blink_cursors(epoch, cx);
5549 }
5550 }
5551
5552 fn blink_cursors(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
5553 if epoch == self.blink_epoch && self.focused && !self.blinking_paused {
5554 self.show_local_cursors = !self.show_local_cursors;
5555 cx.notify();
5556
5557 let epoch = self.next_blink_epoch();
5558 cx.spawn(|this, mut cx| {
5559 let this = this.downgrade();
5560 async move {
5561 Timer::after(CURSOR_BLINK_INTERVAL).await;
5562 if let Some(this) = this.upgrade(&cx) {
5563 this.update(&mut cx, |this, cx| this.blink_cursors(epoch, cx));
5564 }
5565 }
5566 })
5567 .detach();
5568 }
5569 }
5570
5571 pub fn show_local_cursors(&self) -> bool {
5572 self.show_local_cursors && self.focused
5573 }
5574
5575 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
5576 cx.notify();
5577 }
5578
5579 fn on_buffer_event(
5580 &mut self,
5581 _: ModelHandle<MultiBuffer>,
5582 event: &language::Event,
5583 cx: &mut ViewContext<Self>,
5584 ) {
5585 match event {
5586 language::Event::Edited => {
5587 self.refresh_active_diagnostics(cx);
5588 self.refresh_code_actions(cx);
5589 cx.emit(Event::BufferEdited);
5590 }
5591 language::Event::Reparsed => cx.emit(Event::Reparsed),
5592 language::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
5593 language::Event::Saved => cx.emit(Event::Saved),
5594 language::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
5595 language::Event::Reloaded => cx.emit(Event::TitleChanged),
5596 language::Event::Closed => cx.emit(Event::Closed),
5597 language::Event::DiagnosticsUpdated => {
5598 self.refresh_active_diagnostics(cx);
5599 }
5600 _ => {}
5601 }
5602 }
5603
5604 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
5605 cx.notify();
5606 }
5607
5608 pub fn set_searchable(&mut self, searchable: bool) {
5609 self.searchable = searchable;
5610 }
5611
5612 pub fn searchable(&self) -> bool {
5613 self.searchable
5614 }
5615
5616 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
5617 let active_item = workspace.active_item(cx);
5618 let editor_handle = if let Some(editor) = active_item
5619 .as_ref()
5620 .and_then(|item| item.act_as::<Self>(cx))
5621 {
5622 editor
5623 } else {
5624 cx.propagate_action();
5625 return;
5626 };
5627
5628 let editor = editor_handle.read(cx);
5629 let buffer = editor.buffer.read(cx);
5630 if buffer.is_singleton() {
5631 cx.propagate_action();
5632 return;
5633 }
5634
5635 let mut new_selections_by_buffer = HashMap::default();
5636 for selection in editor.selections.all::<usize>(cx) {
5637 for (buffer, mut range) in
5638 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
5639 {
5640 if selection.reversed {
5641 mem::swap(&mut range.start, &mut range.end);
5642 }
5643 new_selections_by_buffer
5644 .entry(buffer)
5645 .or_insert(Vec::new())
5646 .push(range)
5647 }
5648 }
5649
5650 editor_handle.update(cx, |editor, cx| {
5651 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
5652 });
5653 let pane = workspace.active_pane().clone();
5654 pane.update(cx, |pane, _| pane.disable_history());
5655
5656 // We defer the pane interaction because we ourselves are a workspace item
5657 // and activating a new item causes the pane to call a method on us reentrantly,
5658 // which panics if we're on the stack.
5659 cx.defer(move |workspace, cx| {
5660 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
5661 let editor = workspace.open_project_item::<Self>(buffer, cx);
5662 editor.update(cx, |editor, cx| {
5663 editor.change_selections(Some(Autoscroll::Newest), cx, |s| {
5664 s.select_ranges(ranges);
5665 });
5666 });
5667 }
5668
5669 pane.update(cx, |pane, _| pane.enable_history());
5670 });
5671 }
5672
5673 fn jump(workspace: &mut Workspace, action: &Jump, cx: &mut ViewContext<Workspace>) {
5674 let editor = workspace.open_path(action.path.clone(), true, cx);
5675 let position = action.position;
5676 let anchor = action.anchor;
5677 cx.spawn_weak(|_, mut cx| async move {
5678 let editor = editor.await.log_err()?.downcast::<Editor>()?;
5679 editor.update(&mut cx, |editor, cx| {
5680 let buffer = editor.buffer().read(cx).as_singleton()?;
5681 let buffer = buffer.read(cx);
5682 let cursor = if buffer.can_resolve(&anchor) {
5683 language::ToPoint::to_point(&anchor, buffer)
5684 } else {
5685 buffer.clip_point(position, Bias::Left)
5686 };
5687
5688 let nav_history = editor.nav_history.take();
5689 editor.change_selections(Some(Autoscroll::Newest), cx, |s| {
5690 s.select_ranges([cursor..cursor]);
5691 });
5692 editor.nav_history = nav_history;
5693
5694 Some(())
5695 })?;
5696 Some(())
5697 })
5698 .detach()
5699 }
5700}
5701
5702impl EditorSnapshot {
5703 pub fn is_focused(&self) -> bool {
5704 self.is_focused
5705 }
5706
5707 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
5708 self.placeholder_text.as_ref()
5709 }
5710
5711 pub fn scroll_position(&self) -> Vector2F {
5712 compute_scroll_position(
5713 &self.display_snapshot,
5714 self.scroll_position,
5715 &self.scroll_top_anchor,
5716 )
5717 }
5718}
5719
5720impl Deref for EditorSnapshot {
5721 type Target = DisplaySnapshot;
5722
5723 fn deref(&self) -> &Self::Target {
5724 &self.display_snapshot
5725 }
5726}
5727
5728fn compute_scroll_position(
5729 snapshot: &DisplaySnapshot,
5730 mut scroll_position: Vector2F,
5731 scroll_top_anchor: &Anchor,
5732) -> Vector2F {
5733 if *scroll_top_anchor != Anchor::min() {
5734 let scroll_top = scroll_top_anchor.to_display_point(snapshot).row() as f32;
5735 scroll_position.set_y(scroll_top + scroll_position.y());
5736 } else {
5737 scroll_position.set_y(0.);
5738 }
5739 scroll_position
5740}
5741
5742#[derive(Copy, Clone, Debug, PartialEq, Eq)]
5743pub enum Event {
5744 Activate,
5745 BufferEdited,
5746 Edited,
5747 Reparsed,
5748 Blurred,
5749 DirtyChanged,
5750 Saved,
5751 TitleChanged,
5752 SelectionsChanged { local: bool },
5753 ScrollPositionChanged { local: bool },
5754 Closed,
5755}
5756
5757pub struct EditorFocused(pub ViewHandle<Editor>);
5758pub struct EditorBlurred(pub ViewHandle<Editor>);
5759pub struct EditorReleased(pub WeakViewHandle<Editor>);
5760
5761impl Entity for Editor {
5762 type Event = Event;
5763
5764 fn release(&mut self, cx: &mut MutableAppContext) {
5765 cx.emit_global(EditorReleased(self.handle.clone()));
5766 }
5767}
5768
5769impl View for Editor {
5770 fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
5771 let style = self.style(cx);
5772 let font_changed = self.display_map.update(cx, |map, cx| {
5773 map.set_font(style.text.font_id, style.text.font_size, cx)
5774 });
5775
5776 if font_changed {
5777 let handle = self.handle.clone();
5778 cx.defer(move |cx| {
5779 if let Some(editor) = handle.upgrade(cx) {
5780 editor.update(cx, |editor, cx| {
5781 hide_hover(editor, cx);
5782 })
5783 }
5784 });
5785 }
5786
5787 EditorElement::new(self.handle.clone(), style.clone(), self.cursor_shape).boxed()
5788 }
5789
5790 fn ui_name() -> &'static str {
5791 "Editor"
5792 }
5793
5794 fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
5795 let focused_event = EditorFocused(cx.handle());
5796 cx.emit_global(focused_event);
5797 if let Some(rename) = self.pending_rename.as_ref() {
5798 cx.focus(&rename.editor);
5799 } else {
5800 self.focused = true;
5801 self.blink_cursors(self.blink_epoch, cx);
5802 self.buffer.update(cx, |buffer, cx| {
5803 buffer.finalize_last_transaction(cx);
5804 if self.leader_replica_id.is_none() {
5805 buffer.set_active_selections(
5806 &self.selections.disjoint_anchors(),
5807 self.selections.line_mode,
5808 cx,
5809 );
5810 }
5811 });
5812 }
5813 }
5814
5815 fn on_blur(&mut self, cx: &mut ViewContext<Self>) {
5816 let blurred_event = EditorBlurred(cx.handle());
5817 cx.emit_global(blurred_event);
5818 self.focused = false;
5819 self.buffer
5820 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
5821 self.hide_context_menu(cx);
5822 hide_hover(self, cx);
5823 cx.emit(Event::Blurred);
5824 cx.notify();
5825 }
5826
5827 fn keymap_context(&self, _: &AppContext) -> gpui::keymap::Context {
5828 let mut context = Self::default_keymap_context();
5829 let mode = match self.mode {
5830 EditorMode::SingleLine => "single_line",
5831 EditorMode::AutoHeight { .. } => "auto_height",
5832 EditorMode::Full => "full",
5833 };
5834 context.map.insert("mode".into(), mode.into());
5835 if self.pending_rename.is_some() {
5836 context.set.insert("renaming".into());
5837 }
5838 match self.context_menu.as_ref() {
5839 Some(ContextMenu::Completions(_)) => {
5840 context.set.insert("showing_completions".into());
5841 }
5842 Some(ContextMenu::CodeActions(_)) => {
5843 context.set.insert("showing_code_actions".into());
5844 }
5845 None => {}
5846 }
5847
5848 for layer in self.keymap_context_layers.values() {
5849 context.extend(layer);
5850 }
5851
5852 context
5853 }
5854}
5855
5856fn build_style(
5857 settings: &Settings,
5858 get_field_editor_theme: Option<GetFieldEditorTheme>,
5859 override_text_style: Option<&OverrideTextStyle>,
5860 cx: &AppContext,
5861) -> EditorStyle {
5862 let font_cache = cx.font_cache();
5863
5864 let mut theme = settings.theme.editor.clone();
5865 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
5866 let field_editor_theme = get_field_editor_theme(&settings.theme);
5867 theme.text_color = field_editor_theme.text.color;
5868 theme.selection = field_editor_theme.selection;
5869 theme.background = field_editor_theme
5870 .container
5871 .background_color
5872 .unwrap_or_default();
5873 EditorStyle {
5874 text: field_editor_theme.text,
5875 placeholder_text: field_editor_theme.placeholder_text,
5876 theme,
5877 }
5878 } else {
5879 let font_family_id = settings.buffer_font_family;
5880 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
5881 let font_properties = Default::default();
5882 let font_id = font_cache
5883 .select_font(font_family_id, &font_properties)
5884 .unwrap();
5885 let font_size = settings.buffer_font_size;
5886 EditorStyle {
5887 text: TextStyle {
5888 color: settings.theme.editor.text_color,
5889 font_family_name,
5890 font_family_id,
5891 font_id,
5892 font_size,
5893 font_properties,
5894 underline: Default::default(),
5895 },
5896 placeholder_text: None,
5897 theme,
5898 }
5899 };
5900
5901 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
5902 if let Some(highlighted) = style
5903 .text
5904 .clone()
5905 .highlight(highlight_style, font_cache)
5906 .log_err()
5907 {
5908 style.text = highlighted;
5909 }
5910 }
5911
5912 style
5913}
5914
5915trait SelectionExt {
5916 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
5917 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
5918 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
5919 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
5920 -> Range<u32>;
5921}
5922
5923impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
5924 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
5925 let start = self.start.to_point(buffer);
5926 let end = self.end.to_point(buffer);
5927 if self.reversed {
5928 end..start
5929 } else {
5930 start..end
5931 }
5932 }
5933
5934 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
5935 let start = self.start.to_offset(buffer);
5936 let end = self.end.to_offset(buffer);
5937 if self.reversed {
5938 end..start
5939 } else {
5940 start..end
5941 }
5942 }
5943
5944 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
5945 let start = self
5946 .start
5947 .to_point(&map.buffer_snapshot)
5948 .to_display_point(map);
5949 let end = self
5950 .end
5951 .to_point(&map.buffer_snapshot)
5952 .to_display_point(map);
5953 if self.reversed {
5954 end..start
5955 } else {
5956 start..end
5957 }
5958 }
5959
5960 fn spanned_rows(
5961 &self,
5962 include_end_if_at_line_start: bool,
5963 map: &DisplaySnapshot,
5964 ) -> Range<u32> {
5965 let start = self.start.to_point(&map.buffer_snapshot);
5966 let mut end = self.end.to_point(&map.buffer_snapshot);
5967 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
5968 end.row -= 1;
5969 }
5970
5971 let buffer_start = map.prev_line_boundary(start).0;
5972 let buffer_end = map.next_line_boundary(end).0;
5973 buffer_start.row..buffer_end.row + 1
5974 }
5975}
5976
5977impl<T: InvalidationRegion> InvalidationStack<T> {
5978 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
5979 where
5980 S: Clone + ToOffset,
5981 {
5982 while let Some(region) = self.last() {
5983 let all_selections_inside_invalidation_ranges =
5984 if selections.len() == region.ranges().len() {
5985 selections
5986 .iter()
5987 .zip(region.ranges().iter().map(|r| r.to_offset(&buffer)))
5988 .all(|(selection, invalidation_range)| {
5989 let head = selection.head().to_offset(&buffer);
5990 invalidation_range.start <= head && invalidation_range.end >= head
5991 })
5992 } else {
5993 false
5994 };
5995
5996 if all_selections_inside_invalidation_ranges {
5997 break;
5998 } else {
5999 self.pop();
6000 }
6001 }
6002 }
6003}
6004
6005impl<T> Default for InvalidationStack<T> {
6006 fn default() -> Self {
6007 Self(Default::default())
6008 }
6009}
6010
6011impl<T> Deref for InvalidationStack<T> {
6012 type Target = Vec<T>;
6013
6014 fn deref(&self) -> &Self::Target {
6015 &self.0
6016 }
6017}
6018
6019impl<T> DerefMut for InvalidationStack<T> {
6020 fn deref_mut(&mut self) -> &mut Self::Target {
6021 &mut self.0
6022 }
6023}
6024
6025impl InvalidationRegion for BracketPairState {
6026 fn ranges(&self) -> &[Range<Anchor>] {
6027 &self.ranges
6028 }
6029}
6030
6031impl InvalidationRegion for SnippetState {
6032 fn ranges(&self) -> &[Range<Anchor>] {
6033 &self.ranges[self.active_index]
6034 }
6035}
6036
6037impl Deref for EditorStyle {
6038 type Target = theme::Editor;
6039
6040 fn deref(&self) -> &Self::Target {
6041 &self.theme
6042 }
6043}
6044
6045pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
6046 let mut highlighted_lines = Vec::new();
6047 for line in diagnostic.message.lines() {
6048 highlighted_lines.push(highlight_diagnostic_message(line));
6049 }
6050
6051 Arc::new(move |cx: &mut BlockContext| {
6052 let settings = cx.global::<Settings>();
6053 let theme = &settings.theme.editor;
6054 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
6055 let font_size = (style.text_scale_factor * settings.buffer_font_size).round();
6056 Flex::column()
6057 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
6058 Label::new(
6059 line.clone(),
6060 style.message.clone().with_font_size(font_size),
6061 )
6062 .with_highlights(highlights.clone())
6063 .contained()
6064 .with_margin_left(cx.anchor_x)
6065 .boxed()
6066 }))
6067 .aligned()
6068 .left()
6069 .boxed()
6070 })
6071}
6072
6073pub fn highlight_diagnostic_message(message: &str) -> (String, Vec<usize>) {
6074 let mut message_without_backticks = String::new();
6075 let mut prev_offset = 0;
6076 let mut inside_block = false;
6077 let mut highlights = Vec::new();
6078 for (match_ix, (offset, _)) in message
6079 .match_indices('`')
6080 .chain([(message.len(), "")])
6081 .enumerate()
6082 {
6083 message_without_backticks.push_str(&message[prev_offset..offset]);
6084 if inside_block {
6085 highlights.extend(prev_offset - match_ix..offset - match_ix);
6086 }
6087
6088 inside_block = !inside_block;
6089 prev_offset = offset + 1;
6090 }
6091
6092 (message_without_backticks, highlights)
6093}
6094
6095pub fn diagnostic_style(
6096 severity: DiagnosticSeverity,
6097 valid: bool,
6098 theme: &theme::Editor,
6099) -> DiagnosticStyle {
6100 match (severity, valid) {
6101 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
6102 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
6103 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
6104 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
6105 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
6106 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
6107 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
6108 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
6109 _ => theme.invalid_hint_diagnostic.clone(),
6110 }
6111}
6112
6113pub fn combine_syntax_and_fuzzy_match_highlights(
6114 text: &str,
6115 default_style: HighlightStyle,
6116 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
6117 match_indices: &[usize],
6118) -> Vec<(Range<usize>, HighlightStyle)> {
6119 let mut result = Vec::new();
6120 let mut match_indices = match_indices.iter().copied().peekable();
6121
6122 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
6123 {
6124 syntax_highlight.weight = None;
6125
6126 // Add highlights for any fuzzy match characters before the next
6127 // syntax highlight range.
6128 while let Some(&match_index) = match_indices.peek() {
6129 if match_index >= range.start {
6130 break;
6131 }
6132 match_indices.next();
6133 let end_index = char_ix_after(match_index, text);
6134 let mut match_style = default_style;
6135 match_style.weight = Some(fonts::Weight::BOLD);
6136 result.push((match_index..end_index, match_style));
6137 }
6138
6139 if range.start == usize::MAX {
6140 break;
6141 }
6142
6143 // Add highlights for any fuzzy match characters within the
6144 // syntax highlight range.
6145 let mut offset = range.start;
6146 while let Some(&match_index) = match_indices.peek() {
6147 if match_index >= range.end {
6148 break;
6149 }
6150
6151 match_indices.next();
6152 if match_index > offset {
6153 result.push((offset..match_index, syntax_highlight));
6154 }
6155
6156 let mut end_index = char_ix_after(match_index, text);
6157 while let Some(&next_match_index) = match_indices.peek() {
6158 if next_match_index == end_index && next_match_index < range.end {
6159 end_index = char_ix_after(next_match_index, text);
6160 match_indices.next();
6161 } else {
6162 break;
6163 }
6164 }
6165
6166 let mut match_style = syntax_highlight;
6167 match_style.weight = Some(fonts::Weight::BOLD);
6168 result.push((match_index..end_index, match_style));
6169 offset = end_index;
6170 }
6171
6172 if offset < range.end {
6173 result.push((offset..range.end, syntax_highlight));
6174 }
6175 }
6176
6177 fn char_ix_after(ix: usize, text: &str) -> usize {
6178 ix + text[ix..].chars().next().unwrap().len_utf8()
6179 }
6180
6181 result
6182}
6183
6184pub fn styled_runs_for_code_label<'a>(
6185 label: &'a CodeLabel,
6186 syntax_theme: &'a theme::SyntaxTheme,
6187) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
6188 let fade_out = HighlightStyle {
6189 fade_out: Some(0.35),
6190 ..Default::default()
6191 };
6192
6193 let mut prev_end = label.filter_range.end;
6194 label
6195 .runs
6196 .iter()
6197 .enumerate()
6198 .flat_map(move |(ix, (range, highlight_id))| {
6199 let style = if let Some(style) = highlight_id.style(syntax_theme) {
6200 style
6201 } else {
6202 return Default::default();
6203 };
6204 let mut muted_style = style.clone();
6205 muted_style.highlight(fade_out);
6206
6207 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
6208 if range.start >= label.filter_range.end {
6209 if range.start > prev_end {
6210 runs.push((prev_end..range.start, fade_out));
6211 }
6212 runs.push((range.clone(), muted_style));
6213 } else if range.end <= label.filter_range.end {
6214 runs.push((range.clone(), style));
6215 } else {
6216 runs.push((range.start..label.filter_range.end, style));
6217 runs.push((label.filter_range.end..range.end, muted_style));
6218 }
6219 prev_end = cmp::max(prev_end, range.end);
6220
6221 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
6222 runs.push((prev_end..label.text.len(), fade_out));
6223 }
6224
6225 runs
6226 })
6227}
6228
6229#[cfg(test)]
6230mod tests {
6231 use crate::test::{
6232 assert_text_with_selections, build_editor, select_ranges, EditorLspTestContext,
6233 EditorTestContext,
6234 };
6235
6236 use super::*;
6237 use futures::StreamExt;
6238 use gpui::{
6239 geometry::rect::RectF,
6240 platform::{WindowBounds, WindowOptions},
6241 };
6242 use indoc::indoc;
6243 use language::{FakeLspAdapter, LanguageConfig};
6244 use project::FakeFs;
6245 use settings::EditorSettings;
6246 use std::{cell::RefCell, rc::Rc, time::Instant};
6247 use text::Point;
6248 use unindent::Unindent;
6249 use util::{
6250 assert_set_eq,
6251 test::{
6252 marked_text_by, marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker,
6253 },
6254 };
6255 use workspace::{FollowableItem, ItemHandle, NavigationEntry, Pane};
6256
6257 #[gpui::test]
6258 fn test_edit_events(cx: &mut MutableAppContext) {
6259 cx.set_global(Settings::test(cx));
6260 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6261
6262 let events = Rc::new(RefCell::new(Vec::new()));
6263 let (_, editor1) = cx.add_window(Default::default(), {
6264 let events = events.clone();
6265 |cx| {
6266 cx.subscribe(&cx.handle(), move |_, _, event, _| {
6267 if matches!(
6268 event,
6269 Event::Edited | Event::BufferEdited | Event::DirtyChanged
6270 ) {
6271 events.borrow_mut().push(("editor1", *event));
6272 }
6273 })
6274 .detach();
6275 Editor::for_buffer(buffer.clone(), None, cx)
6276 }
6277 });
6278 let (_, editor2) = cx.add_window(Default::default(), {
6279 let events = events.clone();
6280 |cx| {
6281 cx.subscribe(&cx.handle(), move |_, _, event, _| {
6282 if matches!(
6283 event,
6284 Event::Edited | Event::BufferEdited | Event::DirtyChanged
6285 ) {
6286 events.borrow_mut().push(("editor2", *event));
6287 }
6288 })
6289 .detach();
6290 Editor::for_buffer(buffer.clone(), None, cx)
6291 }
6292 });
6293 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6294
6295 // Mutating editor 1 will emit an `Edited` event only for that editor.
6296 editor1.update(cx, |editor, cx| editor.insert("X", cx));
6297 assert_eq!(
6298 mem::take(&mut *events.borrow_mut()),
6299 [
6300 ("editor1", Event::Edited),
6301 ("editor1", Event::BufferEdited),
6302 ("editor2", Event::BufferEdited),
6303 ("editor1", Event::DirtyChanged),
6304 ("editor2", Event::DirtyChanged)
6305 ]
6306 );
6307
6308 // Mutating editor 2 will emit an `Edited` event only for that editor.
6309 editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
6310 assert_eq!(
6311 mem::take(&mut *events.borrow_mut()),
6312 [
6313 ("editor2", Event::Edited),
6314 ("editor1", Event::BufferEdited),
6315 ("editor2", Event::BufferEdited),
6316 ]
6317 );
6318
6319 // Undoing on editor 1 will emit an `Edited` event only for that editor.
6320 editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
6321 assert_eq!(
6322 mem::take(&mut *events.borrow_mut()),
6323 [
6324 ("editor1", Event::Edited),
6325 ("editor1", Event::BufferEdited),
6326 ("editor2", Event::BufferEdited),
6327 ("editor1", Event::DirtyChanged),
6328 ("editor2", Event::DirtyChanged),
6329 ]
6330 );
6331
6332 // Redoing on editor 1 will emit an `Edited` event only for that editor.
6333 editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
6334 assert_eq!(
6335 mem::take(&mut *events.borrow_mut()),
6336 [
6337 ("editor1", Event::Edited),
6338 ("editor1", Event::BufferEdited),
6339 ("editor2", Event::BufferEdited),
6340 ("editor1", Event::DirtyChanged),
6341 ("editor2", Event::DirtyChanged),
6342 ]
6343 );
6344
6345 // Undoing on editor 2 will emit an `Edited` event only for that editor.
6346 editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
6347 assert_eq!(
6348 mem::take(&mut *events.borrow_mut()),
6349 [
6350 ("editor2", Event::Edited),
6351 ("editor1", Event::BufferEdited),
6352 ("editor2", Event::BufferEdited),
6353 ("editor1", Event::DirtyChanged),
6354 ("editor2", Event::DirtyChanged),
6355 ]
6356 );
6357
6358 // Redoing on editor 2 will emit an `Edited` event only for that editor.
6359 editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
6360 assert_eq!(
6361 mem::take(&mut *events.borrow_mut()),
6362 [
6363 ("editor2", Event::Edited),
6364 ("editor1", Event::BufferEdited),
6365 ("editor2", Event::BufferEdited),
6366 ("editor1", Event::DirtyChanged),
6367 ("editor2", Event::DirtyChanged),
6368 ]
6369 );
6370
6371 // No event is emitted when the mutation is a no-op.
6372 editor2.update(cx, |editor, cx| {
6373 editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
6374
6375 editor.backspace(&Backspace, cx);
6376 });
6377 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6378 }
6379
6380 #[gpui::test]
6381 fn test_undo_redo_with_selection_restoration(cx: &mut MutableAppContext) {
6382 cx.set_global(Settings::test(cx));
6383 let mut now = Instant::now();
6384 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6385 let group_interval = buffer.read(cx).transaction_group_interval();
6386 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
6387 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6388
6389 editor.update(cx, |editor, cx| {
6390 editor.start_transaction_at(now, cx);
6391 editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
6392
6393 editor.insert("cd", cx);
6394 editor.end_transaction_at(now, cx);
6395 assert_eq!(editor.text(cx), "12cd56");
6396 assert_eq!(editor.selections.ranges(cx), vec![4..4]);
6397
6398 editor.start_transaction_at(now, cx);
6399 editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
6400 editor.insert("e", cx);
6401 editor.end_transaction_at(now, cx);
6402 assert_eq!(editor.text(cx), "12cde6");
6403 assert_eq!(editor.selections.ranges(cx), vec![5..5]);
6404
6405 now += group_interval + Duration::from_millis(1);
6406 editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
6407
6408 // Simulate an edit in another editor
6409 buffer.update(cx, |buffer, cx| {
6410 buffer.start_transaction_at(now, cx);
6411 buffer.edit([(0..1, "a")], cx);
6412 buffer.edit([(1..1, "b")], cx);
6413 buffer.end_transaction_at(now, cx);
6414 });
6415
6416 assert_eq!(editor.text(cx), "ab2cde6");
6417 assert_eq!(editor.selections.ranges(cx), vec![3..3]);
6418
6419 // Last transaction happened past the group interval in a different editor.
6420 // Undo it individually and don't restore selections.
6421 editor.undo(&Undo, cx);
6422 assert_eq!(editor.text(cx), "12cde6");
6423 assert_eq!(editor.selections.ranges(cx), vec![2..2]);
6424
6425 // First two transactions happened within the group interval in this editor.
6426 // Undo them together and restore selections.
6427 editor.undo(&Undo, cx);
6428 editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
6429 assert_eq!(editor.text(cx), "123456");
6430 assert_eq!(editor.selections.ranges(cx), vec![0..0]);
6431
6432 // Redo the first two transactions together.
6433 editor.redo(&Redo, cx);
6434 assert_eq!(editor.text(cx), "12cde6");
6435 assert_eq!(editor.selections.ranges(cx), vec![5..5]);
6436
6437 // Redo the last transaction on its own.
6438 editor.redo(&Redo, cx);
6439 assert_eq!(editor.text(cx), "ab2cde6");
6440 assert_eq!(editor.selections.ranges(cx), vec![6..6]);
6441
6442 // Test empty transactions.
6443 editor.start_transaction_at(now, cx);
6444 editor.end_transaction_at(now, cx);
6445 editor.undo(&Undo, cx);
6446 assert_eq!(editor.text(cx), "12cde6");
6447 });
6448 }
6449
6450 #[gpui::test]
6451 fn test_selection_with_mouse(cx: &mut gpui::MutableAppContext) {
6452 cx.set_global(Settings::test(cx));
6453
6454 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
6455 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6456 editor.update(cx, |view, cx| {
6457 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
6458 });
6459 assert_eq!(
6460 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6461 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
6462 );
6463
6464 editor.update(cx, |view, cx| {
6465 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6466 });
6467
6468 assert_eq!(
6469 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6470 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6471 );
6472
6473 editor.update(cx, |view, cx| {
6474 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6475 });
6476
6477 assert_eq!(
6478 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6479 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
6480 );
6481
6482 editor.update(cx, |view, cx| {
6483 view.end_selection(cx);
6484 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6485 });
6486
6487 assert_eq!(
6488 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6489 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
6490 );
6491
6492 editor.update(cx, |view, cx| {
6493 view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx);
6494 view.update_selection(DisplayPoint::new(0, 0), 0, Vector2F::zero(), cx);
6495 });
6496
6497 assert_eq!(
6498 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6499 [
6500 DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
6501 DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
6502 ]
6503 );
6504
6505 editor.update(cx, |view, cx| {
6506 view.end_selection(cx);
6507 });
6508
6509 assert_eq!(
6510 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6511 [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
6512 );
6513 }
6514
6515 #[gpui::test]
6516 fn test_canceling_pending_selection(cx: &mut gpui::MutableAppContext) {
6517 cx.set_global(Settings::test(cx));
6518 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
6519 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6520
6521 view.update(cx, |view, cx| {
6522 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
6523 assert_eq!(
6524 view.selections.display_ranges(cx),
6525 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
6526 );
6527 });
6528
6529 view.update(cx, |view, cx| {
6530 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6531 assert_eq!(
6532 view.selections.display_ranges(cx),
6533 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6534 );
6535 });
6536
6537 view.update(cx, |view, cx| {
6538 view.cancel(&Cancel, cx);
6539 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6540 assert_eq!(
6541 view.selections.display_ranges(cx),
6542 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6543 );
6544 });
6545 }
6546
6547 #[gpui::test]
6548 fn test_clone(cx: &mut gpui::MutableAppContext) {
6549 let (text, selection_ranges) = marked_text_ranges(indoc! {"
6550 one
6551 two
6552 three[]
6553 four
6554 five[]
6555 "});
6556 cx.set_global(Settings::test(cx));
6557 let buffer = MultiBuffer::build_simple(&text, cx);
6558
6559 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6560
6561 editor.update(cx, |editor, cx| {
6562 editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
6563 editor.fold_ranges(
6564 [
6565 Point::new(1, 0)..Point::new(2, 0),
6566 Point::new(3, 0)..Point::new(4, 0),
6567 ],
6568 cx,
6569 );
6570 });
6571
6572 let (_, cloned_editor) = editor.update(cx, |editor, cx| {
6573 cx.add_window(Default::default(), |cx| editor.clone(cx))
6574 });
6575
6576 let snapshot = editor.update(cx, |e, cx| e.snapshot(cx));
6577 let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx));
6578
6579 assert_eq!(
6580 cloned_editor.update(cx, |e, cx| e.display_text(cx)),
6581 editor.update(cx, |e, cx| e.display_text(cx))
6582 );
6583 assert_eq!(
6584 cloned_snapshot
6585 .folds_in_range(0..text.len())
6586 .collect::<Vec<_>>(),
6587 snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
6588 );
6589 assert_set_eq!(
6590 cloned_editor.read(cx).selections.ranges::<Point>(cx),
6591 editor.read(cx).selections.ranges(cx)
6592 );
6593 assert_set_eq!(
6594 cloned_editor.update(cx, |e, cx| e.selections.display_ranges(cx)),
6595 editor.update(cx, |e, cx| e.selections.display_ranges(cx))
6596 );
6597 }
6598
6599 #[gpui::test]
6600 fn test_navigation_history(cx: &mut gpui::MutableAppContext) {
6601 cx.set_global(Settings::test(cx));
6602 use workspace::Item;
6603 let pane = cx.add_view(Default::default(), |cx| Pane::new(cx));
6604 let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
6605
6606 cx.add_window(Default::default(), |cx| {
6607 let mut editor = build_editor(buffer.clone(), cx);
6608 let handle = cx.handle();
6609 editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
6610
6611 fn pop_history(
6612 editor: &mut Editor,
6613 cx: &mut MutableAppContext,
6614 ) -> Option<NavigationEntry> {
6615 editor.nav_history.as_mut().unwrap().pop_backward(cx)
6616 }
6617
6618 // Move the cursor a small distance.
6619 // Nothing is added to the navigation history.
6620 editor.change_selections(None, cx, |s| {
6621 s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)])
6622 });
6623 editor.change_selections(None, cx, |s| {
6624 s.select_display_ranges([DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)])
6625 });
6626 assert!(pop_history(&mut editor, cx).is_none());
6627
6628 // Move the cursor a large distance.
6629 // The history can jump back to the previous position.
6630 editor.change_selections(None, cx, |s| {
6631 s.select_display_ranges([DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)])
6632 });
6633 let nav_entry = pop_history(&mut editor, cx).unwrap();
6634 editor.navigate(nav_entry.data.unwrap(), cx);
6635 assert_eq!(nav_entry.item.id(), cx.view_id());
6636 assert_eq!(
6637 editor.selections.display_ranges(cx),
6638 &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]
6639 );
6640 assert!(pop_history(&mut editor, cx).is_none());
6641
6642 // Move the cursor a small distance via the mouse.
6643 // Nothing is added to the navigation history.
6644 editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx);
6645 editor.end_selection(cx);
6646 assert_eq!(
6647 editor.selections.display_ranges(cx),
6648 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
6649 );
6650 assert!(pop_history(&mut editor, cx).is_none());
6651
6652 // Move the cursor a large distance via the mouse.
6653 // The history can jump back to the previous position.
6654 editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx);
6655 editor.end_selection(cx);
6656 assert_eq!(
6657 editor.selections.display_ranges(cx),
6658 &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]
6659 );
6660 let nav_entry = pop_history(&mut editor, cx).unwrap();
6661 editor.navigate(nav_entry.data.unwrap(), cx);
6662 assert_eq!(nav_entry.item.id(), cx.view_id());
6663 assert_eq!(
6664 editor.selections.display_ranges(cx),
6665 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
6666 );
6667 assert!(pop_history(&mut editor, cx).is_none());
6668
6669 // Set scroll position to check later
6670 editor.set_scroll_position(Vector2F::new(5.5, 5.5), cx);
6671 let original_scroll_position = editor.scroll_position;
6672 let original_scroll_top_anchor = editor.scroll_top_anchor.clone();
6673
6674 // Jump to the end of the document and adjust scroll
6675 editor.move_to_end(&MoveToEnd, cx);
6676 editor.set_scroll_position(Vector2F::new(-2.5, -0.5), cx);
6677 assert_ne!(editor.scroll_position, original_scroll_position);
6678 assert_ne!(editor.scroll_top_anchor, original_scroll_top_anchor);
6679
6680 let nav_entry = pop_history(&mut editor, cx).unwrap();
6681 editor.navigate(nav_entry.data.unwrap(), cx);
6682 assert_eq!(editor.scroll_position, original_scroll_position);
6683 assert_eq!(editor.scroll_top_anchor, original_scroll_top_anchor);
6684
6685 // Ensure we don't panic when navigation data contains invalid anchors *and* points.
6686 let mut invalid_anchor = editor.scroll_top_anchor.clone();
6687 invalid_anchor.text_anchor.buffer_id = Some(999);
6688 let invalid_point = Point::new(9999, 0);
6689 editor.navigate(
6690 Box::new(NavigationData {
6691 cursor_anchor: invalid_anchor.clone(),
6692 cursor_position: invalid_point,
6693 scroll_top_anchor: invalid_anchor.clone(),
6694 scroll_top_row: invalid_point.row,
6695 scroll_position: Default::default(),
6696 }),
6697 cx,
6698 );
6699 assert_eq!(
6700 editor.selections.display_ranges(cx),
6701 &[editor.max_point(cx)..editor.max_point(cx)]
6702 );
6703 assert_eq!(
6704 editor.scroll_position(cx),
6705 vec2f(0., editor.max_point(cx).row() as f32)
6706 );
6707
6708 editor
6709 });
6710 }
6711
6712 #[gpui::test]
6713 fn test_cancel(cx: &mut gpui::MutableAppContext) {
6714 cx.set_global(Settings::test(cx));
6715 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
6716 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6717
6718 view.update(cx, |view, cx| {
6719 view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
6720 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6721 view.end_selection(cx);
6722
6723 view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx);
6724 view.update_selection(DisplayPoint::new(0, 3), 0, Vector2F::zero(), cx);
6725 view.end_selection(cx);
6726 assert_eq!(
6727 view.selections.display_ranges(cx),
6728 [
6729 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
6730 DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1),
6731 ]
6732 );
6733 });
6734
6735 view.update(cx, |view, cx| {
6736 view.cancel(&Cancel, cx);
6737 assert_eq!(
6738 view.selections.display_ranges(cx),
6739 [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)]
6740 );
6741 });
6742
6743 view.update(cx, |view, cx| {
6744 view.cancel(&Cancel, cx);
6745 assert_eq!(
6746 view.selections.display_ranges(cx),
6747 [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)]
6748 );
6749 });
6750 }
6751
6752 #[gpui::test]
6753 fn test_fold(cx: &mut gpui::MutableAppContext) {
6754 cx.set_global(Settings::test(cx));
6755 let buffer = MultiBuffer::build_simple(
6756 &"
6757 impl Foo {
6758 // Hello!
6759
6760 fn a() {
6761 1
6762 }
6763
6764 fn b() {
6765 2
6766 }
6767
6768 fn c() {
6769 3
6770 }
6771 }
6772 "
6773 .unindent(),
6774 cx,
6775 );
6776 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6777
6778 view.update(cx, |view, cx| {
6779 view.change_selections(None, cx, |s| {
6780 s.select_display_ranges([DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)]);
6781 });
6782 view.fold(&Fold, cx);
6783 assert_eq!(
6784 view.display_text(cx),
6785 "
6786 impl Foo {
6787 // Hello!
6788
6789 fn a() {
6790 1
6791 }
6792
6793 fn b() {…
6794 }
6795
6796 fn c() {…
6797 }
6798 }
6799 "
6800 .unindent(),
6801 );
6802
6803 view.fold(&Fold, cx);
6804 assert_eq!(
6805 view.display_text(cx),
6806 "
6807 impl Foo {…
6808 }
6809 "
6810 .unindent(),
6811 );
6812
6813 view.unfold_lines(&UnfoldLines, cx);
6814 assert_eq!(
6815 view.display_text(cx),
6816 "
6817 impl Foo {
6818 // Hello!
6819
6820 fn a() {
6821 1
6822 }
6823
6824 fn b() {…
6825 }
6826
6827 fn c() {…
6828 }
6829 }
6830 "
6831 .unindent(),
6832 );
6833
6834 view.unfold_lines(&UnfoldLines, cx);
6835 assert_eq!(view.display_text(cx), buffer.read(cx).read(cx).text());
6836 });
6837 }
6838
6839 #[gpui::test]
6840 fn test_move_cursor(cx: &mut gpui::MutableAppContext) {
6841 cx.set_global(Settings::test(cx));
6842 let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
6843 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6844
6845 buffer.update(cx, |buffer, cx| {
6846 buffer.edit(
6847 vec![
6848 (Point::new(1, 0)..Point::new(1, 0), "\t"),
6849 (Point::new(1, 1)..Point::new(1, 1), "\t"),
6850 ],
6851 cx,
6852 );
6853 });
6854
6855 view.update(cx, |view, cx| {
6856 assert_eq!(
6857 view.selections.display_ranges(cx),
6858 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
6859 );
6860
6861 view.move_down(&MoveDown, cx);
6862 assert_eq!(
6863 view.selections.display_ranges(cx),
6864 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
6865 );
6866
6867 view.move_right(&MoveRight, cx);
6868 assert_eq!(
6869 view.selections.display_ranges(cx),
6870 &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
6871 );
6872
6873 view.move_left(&MoveLeft, cx);
6874 assert_eq!(
6875 view.selections.display_ranges(cx),
6876 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
6877 );
6878
6879 view.move_up(&MoveUp, cx);
6880 assert_eq!(
6881 view.selections.display_ranges(cx),
6882 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
6883 );
6884
6885 view.move_to_end(&MoveToEnd, cx);
6886 assert_eq!(
6887 view.selections.display_ranges(cx),
6888 &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)]
6889 );
6890
6891 view.move_to_beginning(&MoveToBeginning, cx);
6892 assert_eq!(
6893 view.selections.display_ranges(cx),
6894 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
6895 );
6896
6897 view.change_selections(None, cx, |s| {
6898 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]);
6899 });
6900 view.select_to_beginning(&SelectToBeginning, cx);
6901 assert_eq!(
6902 view.selections.display_ranges(cx),
6903 &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)]
6904 );
6905
6906 view.select_to_end(&SelectToEnd, cx);
6907 assert_eq!(
6908 view.selections.display_ranges(cx),
6909 &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)]
6910 );
6911 });
6912 }
6913
6914 #[gpui::test]
6915 fn test_move_cursor_multibyte(cx: &mut gpui::MutableAppContext) {
6916 cx.set_global(Settings::test(cx));
6917 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx);
6918 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6919
6920 assert_eq!('ⓐ'.len_utf8(), 3);
6921 assert_eq!('α'.len_utf8(), 2);
6922
6923 view.update(cx, |view, cx| {
6924 view.fold_ranges(
6925 vec![
6926 Point::new(0, 6)..Point::new(0, 12),
6927 Point::new(1, 2)..Point::new(1, 4),
6928 Point::new(2, 4)..Point::new(2, 8),
6929 ],
6930 cx,
6931 );
6932 assert_eq!(view.display_text(cx), "ⓐⓑ…ⓔ\nab…e\nαβ…ε\n");
6933
6934 view.move_right(&MoveRight, cx);
6935 assert_eq!(
6936 view.selections.display_ranges(cx),
6937 &[empty_range(0, "ⓐ".len())]
6938 );
6939 view.move_right(&MoveRight, cx);
6940 assert_eq!(
6941 view.selections.display_ranges(cx),
6942 &[empty_range(0, "ⓐⓑ".len())]
6943 );
6944 view.move_right(&MoveRight, cx);
6945 assert_eq!(
6946 view.selections.display_ranges(cx),
6947 &[empty_range(0, "ⓐⓑ…".len())]
6948 );
6949
6950 view.move_down(&MoveDown, cx);
6951 assert_eq!(
6952 view.selections.display_ranges(cx),
6953 &[empty_range(1, "ab…".len())]
6954 );
6955 view.move_left(&MoveLeft, cx);
6956 assert_eq!(
6957 view.selections.display_ranges(cx),
6958 &[empty_range(1, "ab".len())]
6959 );
6960 view.move_left(&MoveLeft, cx);
6961 assert_eq!(
6962 view.selections.display_ranges(cx),
6963 &[empty_range(1, "a".len())]
6964 );
6965
6966 view.move_down(&MoveDown, cx);
6967 assert_eq!(
6968 view.selections.display_ranges(cx),
6969 &[empty_range(2, "α".len())]
6970 );
6971 view.move_right(&MoveRight, cx);
6972 assert_eq!(
6973 view.selections.display_ranges(cx),
6974 &[empty_range(2, "αβ".len())]
6975 );
6976 view.move_right(&MoveRight, cx);
6977 assert_eq!(
6978 view.selections.display_ranges(cx),
6979 &[empty_range(2, "αβ…".len())]
6980 );
6981 view.move_right(&MoveRight, cx);
6982 assert_eq!(
6983 view.selections.display_ranges(cx),
6984 &[empty_range(2, "αβ…ε".len())]
6985 );
6986
6987 view.move_up(&MoveUp, cx);
6988 assert_eq!(
6989 view.selections.display_ranges(cx),
6990 &[empty_range(1, "ab…e".len())]
6991 );
6992 view.move_up(&MoveUp, cx);
6993 assert_eq!(
6994 view.selections.display_ranges(cx),
6995 &[empty_range(0, "ⓐⓑ…ⓔ".len())]
6996 );
6997 view.move_left(&MoveLeft, cx);
6998 assert_eq!(
6999 view.selections.display_ranges(cx),
7000 &[empty_range(0, "ⓐⓑ…".len())]
7001 );
7002 view.move_left(&MoveLeft, cx);
7003 assert_eq!(
7004 view.selections.display_ranges(cx),
7005 &[empty_range(0, "ⓐⓑ".len())]
7006 );
7007 view.move_left(&MoveLeft, cx);
7008 assert_eq!(
7009 view.selections.display_ranges(cx),
7010 &[empty_range(0, "ⓐ".len())]
7011 );
7012 });
7013 }
7014
7015 #[gpui::test]
7016 fn test_move_cursor_different_line_lengths(cx: &mut gpui::MutableAppContext) {
7017 cx.set_global(Settings::test(cx));
7018 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
7019 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7020 view.update(cx, |view, cx| {
7021 view.change_selections(None, cx, |s| {
7022 s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
7023 });
7024 view.move_down(&MoveDown, cx);
7025 assert_eq!(
7026 view.selections.display_ranges(cx),
7027 &[empty_range(1, "abcd".len())]
7028 );
7029
7030 view.move_down(&MoveDown, cx);
7031 assert_eq!(
7032 view.selections.display_ranges(cx),
7033 &[empty_range(2, "αβγ".len())]
7034 );
7035
7036 view.move_down(&MoveDown, cx);
7037 assert_eq!(
7038 view.selections.display_ranges(cx),
7039 &[empty_range(3, "abcd".len())]
7040 );
7041
7042 view.move_down(&MoveDown, cx);
7043 assert_eq!(
7044 view.selections.display_ranges(cx),
7045 &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
7046 );
7047
7048 view.move_up(&MoveUp, cx);
7049 assert_eq!(
7050 view.selections.display_ranges(cx),
7051 &[empty_range(3, "abcd".len())]
7052 );
7053
7054 view.move_up(&MoveUp, cx);
7055 assert_eq!(
7056 view.selections.display_ranges(cx),
7057 &[empty_range(2, "αβγ".len())]
7058 );
7059 });
7060 }
7061
7062 #[gpui::test]
7063 fn test_beginning_end_of_line(cx: &mut gpui::MutableAppContext) {
7064 cx.set_global(Settings::test(cx));
7065 let buffer = MultiBuffer::build_simple("abc\n def", cx);
7066 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7067 view.update(cx, |view, cx| {
7068 view.change_selections(None, cx, |s| {
7069 s.select_display_ranges([
7070 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7071 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
7072 ]);
7073 });
7074 });
7075
7076 view.update(cx, |view, cx| {
7077 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7078 assert_eq!(
7079 view.selections.display_ranges(cx),
7080 &[
7081 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7082 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7083 ]
7084 );
7085 });
7086
7087 view.update(cx, |view, cx| {
7088 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7089 assert_eq!(
7090 view.selections.display_ranges(cx),
7091 &[
7092 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7093 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7094 ]
7095 );
7096 });
7097
7098 view.update(cx, |view, cx| {
7099 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7100 assert_eq!(
7101 view.selections.display_ranges(cx),
7102 &[
7103 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7104 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7105 ]
7106 );
7107 });
7108
7109 view.update(cx, |view, cx| {
7110 view.move_to_end_of_line(&MoveToEndOfLine, cx);
7111 assert_eq!(
7112 view.selections.display_ranges(cx),
7113 &[
7114 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7115 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
7116 ]
7117 );
7118 });
7119
7120 // Moving to the end of line again is a no-op.
7121 view.update(cx, |view, cx| {
7122 view.move_to_end_of_line(&MoveToEndOfLine, cx);
7123 assert_eq!(
7124 view.selections.display_ranges(cx),
7125 &[
7126 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7127 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
7128 ]
7129 );
7130 });
7131
7132 view.update(cx, |view, cx| {
7133 view.move_left(&MoveLeft, cx);
7134 view.select_to_beginning_of_line(
7135 &SelectToBeginningOfLine {
7136 stop_at_soft_wraps: true,
7137 },
7138 cx,
7139 );
7140 assert_eq!(
7141 view.selections.display_ranges(cx),
7142 &[
7143 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7144 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7145 ]
7146 );
7147 });
7148
7149 view.update(cx, |view, cx| {
7150 view.select_to_beginning_of_line(
7151 &SelectToBeginningOfLine {
7152 stop_at_soft_wraps: true,
7153 },
7154 cx,
7155 );
7156 assert_eq!(
7157 view.selections.display_ranges(cx),
7158 &[
7159 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7160 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0),
7161 ]
7162 );
7163 });
7164
7165 view.update(cx, |view, cx| {
7166 view.select_to_beginning_of_line(
7167 &SelectToBeginningOfLine {
7168 stop_at_soft_wraps: true,
7169 },
7170 cx,
7171 );
7172 assert_eq!(
7173 view.selections.display_ranges(cx),
7174 &[
7175 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7176 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7177 ]
7178 );
7179 });
7180
7181 view.update(cx, |view, cx| {
7182 view.select_to_end_of_line(
7183 &SelectToEndOfLine {
7184 stop_at_soft_wraps: true,
7185 },
7186 cx,
7187 );
7188 assert_eq!(
7189 view.selections.display_ranges(cx),
7190 &[
7191 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
7192 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5),
7193 ]
7194 );
7195 });
7196
7197 view.update(cx, |view, cx| {
7198 view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
7199 assert_eq!(view.display_text(cx), "ab\n de");
7200 assert_eq!(
7201 view.selections.display_ranges(cx),
7202 &[
7203 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7204 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
7205 ]
7206 );
7207 });
7208
7209 view.update(cx, |view, cx| {
7210 view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
7211 assert_eq!(view.display_text(cx), "\n");
7212 assert_eq!(
7213 view.selections.display_ranges(cx),
7214 &[
7215 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7216 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7217 ]
7218 );
7219 });
7220 }
7221
7222 #[gpui::test]
7223 fn test_prev_next_word_boundary(cx: &mut gpui::MutableAppContext) {
7224 cx.set_global(Settings::test(cx));
7225 let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx);
7226 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7227 view.update(cx, |view, cx| {
7228 view.change_selections(None, cx, |s| {
7229 s.select_display_ranges([
7230 DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
7231 DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
7232 ])
7233 });
7234
7235 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7236 assert_selection_ranges(
7237 "use std::<>str::{foo, bar}\n\n {[]baz.qux()}",
7238 vec![('<', '>'), ('[', ']')],
7239 view,
7240 cx,
7241 );
7242
7243 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7244 assert_selection_ranges(
7245 "use std<>::str::{foo, bar}\n\n []{baz.qux()}",
7246 vec![('<', '>'), ('[', ']')],
7247 view,
7248 cx,
7249 );
7250
7251 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7252 assert_selection_ranges(
7253 "use <>std::str::{foo, bar}\n\n[] {baz.qux()}",
7254 vec![('<', '>'), ('[', ']')],
7255 view,
7256 cx,
7257 );
7258
7259 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7260 assert_selection_ranges(
7261 "<>use std::str::{foo, bar}\n[]\n {baz.qux()}",
7262 vec![('<', '>'), ('[', ']')],
7263 view,
7264 cx,
7265 );
7266
7267 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7268 assert_selection_ranges(
7269 "<>use std::str::{foo, bar[]}\n\n {baz.qux()}",
7270 vec![('<', '>'), ('[', ']')],
7271 view,
7272 cx,
7273 );
7274
7275 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7276 assert_selection_ranges(
7277 "use<> std::str::{foo, bar}[]\n\n {baz.qux()}",
7278 vec![('<', '>'), ('[', ']')],
7279 view,
7280 cx,
7281 );
7282
7283 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7284 assert_selection_ranges(
7285 "use std<>::str::{foo, bar}\n[]\n {baz.qux()}",
7286 vec![('<', '>'), ('[', ']')],
7287 view,
7288 cx,
7289 );
7290
7291 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7292 assert_selection_ranges(
7293 "use std::<>str::{foo, bar}\n\n {[]baz.qux()}",
7294 vec![('<', '>'), ('[', ']')],
7295 view,
7296 cx,
7297 );
7298
7299 view.move_right(&MoveRight, cx);
7300 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7301 assert_selection_ranges(
7302 "use std::>s<tr::{foo, bar}\n\n {]b[az.qux()}",
7303 vec![('<', '>'), ('[', ']')],
7304 view,
7305 cx,
7306 );
7307
7308 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7309 assert_selection_ranges(
7310 "use std>::s<tr::{foo, bar}\n\n ]{b[az.qux()}",
7311 vec![('<', '>'), ('[', ']')],
7312 view,
7313 cx,
7314 );
7315
7316 view.select_to_next_word_end(&SelectToNextWordEnd, cx);
7317 assert_selection_ranges(
7318 "use std::>s<tr::{foo, bar}\n\n {]b[az.qux()}",
7319 vec![('<', '>'), ('[', ']')],
7320 view,
7321 cx,
7322 );
7323 });
7324 }
7325
7326 #[gpui::test]
7327 fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut gpui::MutableAppContext) {
7328 cx.set_global(Settings::test(cx));
7329 let buffer = MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx);
7330 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7331
7332 view.update(cx, |view, cx| {
7333 view.set_wrap_width(Some(140.), cx);
7334 assert_eq!(
7335 view.display_text(cx),
7336 "use one::{\n two::three::\n four::five\n};"
7337 );
7338
7339 view.change_selections(None, cx, |s| {
7340 s.select_display_ranges([DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)]);
7341 });
7342
7343 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7344 assert_eq!(
7345 view.selections.display_ranges(cx),
7346 &[DisplayPoint::new(1, 9)..DisplayPoint::new(1, 9)]
7347 );
7348
7349 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7350 assert_eq!(
7351 view.selections.display_ranges(cx),
7352 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
7353 );
7354
7355 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7356 assert_eq!(
7357 view.selections.display_ranges(cx),
7358 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7359 );
7360
7361 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7362 assert_eq!(
7363 view.selections.display_ranges(cx),
7364 &[DisplayPoint::new(2, 8)..DisplayPoint::new(2, 8)]
7365 );
7366
7367 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7368 assert_eq!(
7369 view.selections.display_ranges(cx),
7370 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7371 );
7372
7373 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7374 assert_eq!(
7375 view.selections.display_ranges(cx),
7376 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
7377 );
7378 });
7379 }
7380
7381 #[gpui::test]
7382 fn test_delete_to_beginning_of_line(cx: &mut gpui::MutableAppContext) {
7383 cx.set_global(Settings::test(cx));
7384 let (text, ranges) = marked_text_ranges("one [two three] four");
7385 let buffer = MultiBuffer::build_simple(&text, cx);
7386
7387 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7388
7389 editor.update(cx, |editor, cx| {
7390 editor.change_selections(None, cx, |s| s.select_ranges(ranges));
7391 editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
7392 assert_eq!(editor.text(cx), " four");
7393 });
7394 }
7395
7396 #[gpui::test]
7397 fn test_delete_to_word_boundary(cx: &mut gpui::MutableAppContext) {
7398 cx.set_global(Settings::test(cx));
7399 let buffer = MultiBuffer::build_simple("one two three four", cx);
7400 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7401
7402 view.update(cx, |view, cx| {
7403 view.change_selections(None, cx, |s| {
7404 s.select_display_ranges([
7405 // an empty selection - the preceding word fragment is deleted
7406 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7407 // characters selected - they are deleted
7408 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 12),
7409 ])
7410 });
7411 view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
7412 });
7413
7414 assert_eq!(buffer.read(cx).read(cx).text(), "e two te four");
7415
7416 view.update(cx, |view, cx| {
7417 view.change_selections(None, cx, |s| {
7418 s.select_display_ranges([
7419 // an empty selection - the following word fragment is deleted
7420 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7421 // characters selected - they are deleted
7422 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 10),
7423 ])
7424 });
7425 view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
7426 });
7427
7428 assert_eq!(buffer.read(cx).read(cx).text(), "e t te our");
7429 }
7430
7431 #[gpui::test]
7432 fn test_newline(cx: &mut gpui::MutableAppContext) {
7433 cx.set_global(Settings::test(cx));
7434 let buffer = MultiBuffer::build_simple("aaaa\n bbbb\n", cx);
7435 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7436
7437 view.update(cx, |view, cx| {
7438 view.change_selections(None, cx, |s| {
7439 s.select_display_ranges([
7440 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7441 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7442 DisplayPoint::new(1, 6)..DisplayPoint::new(1, 6),
7443 ])
7444 });
7445
7446 view.newline(&Newline, cx);
7447 assert_eq!(view.text(cx), "aa\naa\n \n bb\n bb\n");
7448 });
7449 }
7450
7451 #[gpui::test]
7452 fn test_newline_with_old_selections(cx: &mut gpui::MutableAppContext) {
7453 cx.set_global(Settings::test(cx));
7454 let buffer = MultiBuffer::build_simple(
7455 "
7456 a
7457 b(
7458 X
7459 )
7460 c(
7461 X
7462 )
7463 "
7464 .unindent()
7465 .as_str(),
7466 cx,
7467 );
7468
7469 let (_, editor) = cx.add_window(Default::default(), |cx| {
7470 let mut editor = build_editor(buffer.clone(), cx);
7471 editor.change_selections(None, cx, |s| {
7472 s.select_ranges([
7473 Point::new(2, 4)..Point::new(2, 5),
7474 Point::new(5, 4)..Point::new(5, 5),
7475 ])
7476 });
7477 editor
7478 });
7479
7480 // Edit the buffer directly, deleting ranges surrounding the editor's selections
7481 buffer.update(cx, |buffer, cx| {
7482 buffer.edit(
7483 [
7484 (Point::new(1, 2)..Point::new(3, 0), ""),
7485 (Point::new(4, 2)..Point::new(6, 0), ""),
7486 ],
7487 cx,
7488 );
7489 assert_eq!(
7490 buffer.read(cx).text(),
7491 "
7492 a
7493 b()
7494 c()
7495 "
7496 .unindent()
7497 );
7498 });
7499
7500 editor.update(cx, |editor, cx| {
7501 assert_eq!(
7502 editor.selections.ranges(cx),
7503 &[
7504 Point::new(1, 2)..Point::new(1, 2),
7505 Point::new(2, 2)..Point::new(2, 2),
7506 ],
7507 );
7508
7509 editor.newline(&Newline, cx);
7510 assert_eq!(
7511 editor.text(cx),
7512 "
7513 a
7514 b(
7515 )
7516 c(
7517 )
7518 "
7519 .unindent()
7520 );
7521
7522 // The selections are moved after the inserted newlines
7523 assert_eq!(
7524 editor.selections.ranges(cx),
7525 &[
7526 Point::new(2, 0)..Point::new(2, 0),
7527 Point::new(4, 0)..Point::new(4, 0),
7528 ],
7529 );
7530 });
7531 }
7532
7533 #[gpui::test]
7534 fn test_insert_with_old_selections(cx: &mut gpui::MutableAppContext) {
7535 cx.set_global(Settings::test(cx));
7536 let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
7537 let (_, editor) = cx.add_window(Default::default(), |cx| {
7538 let mut editor = build_editor(buffer.clone(), cx);
7539 editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
7540 editor
7541 });
7542
7543 // Edit the buffer directly, deleting ranges surrounding the editor's selections
7544 buffer.update(cx, |buffer, cx| {
7545 buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], cx);
7546 assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
7547 });
7548
7549 editor.update(cx, |editor, cx| {
7550 assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
7551
7552 editor.insert("Z", cx);
7553 assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
7554
7555 // The selections are moved after the inserted characters
7556 assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
7557 });
7558 }
7559
7560 #[gpui::test]
7561 async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
7562 let mut cx = EditorTestContext::new(cx).await;
7563
7564 cx.set_state(indoc! {"
7565 [one} [two}
7566 three
7567 four"});
7568 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7569 cx.assert_editor_state(indoc! {"
7570 [one} [two}
7571 three
7572 four"});
7573
7574 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7575 cx.assert_editor_state(indoc! {"
7576 [one} [two}
7577 three
7578 four"});
7579
7580 // select across line ending
7581 cx.set_state(indoc! {"
7582 one two
7583 t[hree
7584 } four"});
7585 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7586 cx.assert_editor_state(indoc! {"
7587 one two
7588 t[hree
7589 } four"});
7590
7591 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7592 cx.assert_editor_state(indoc! {"
7593 one two
7594 t[hree
7595 } four"});
7596
7597 // Ensure that indenting/outdenting works when the cursor is at column 0.
7598 cx.set_state(indoc! {"
7599 one two
7600 |three
7601 four"});
7602 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7603 cx.assert_editor_state(indoc! {"
7604 one two
7605 |three
7606 four"});
7607
7608 cx.set_state(indoc! {"
7609 one two
7610 | three
7611 four"});
7612 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7613 cx.assert_editor_state(indoc! {"
7614 one two
7615 |three
7616 four"});
7617 }
7618
7619 #[gpui::test]
7620 async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
7621 let mut cx = EditorTestContext::new(cx).await;
7622 cx.update(|cx| {
7623 cx.update_global::<Settings, _, _>(|settings, _| {
7624 settings.editor_overrides.hard_tabs = Some(true);
7625 });
7626 });
7627
7628 // select two ranges on one line
7629 cx.set_state(indoc! {"
7630 [one} [two}
7631 three
7632 four"});
7633 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7634 cx.assert_editor_state(indoc! {"
7635 \t[one} [two}
7636 three
7637 four"});
7638 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7639 cx.assert_editor_state(indoc! {"
7640 \t\t[one} [two}
7641 three
7642 four"});
7643 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7644 cx.assert_editor_state(indoc! {"
7645 \t[one} [two}
7646 three
7647 four"});
7648 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7649 cx.assert_editor_state(indoc! {"
7650 [one} [two}
7651 three
7652 four"});
7653
7654 // select across a line ending
7655 cx.set_state(indoc! {"
7656 one two
7657 t[hree
7658 }four"});
7659 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7660 cx.assert_editor_state(indoc! {"
7661 one two
7662 \tt[hree
7663 }four"});
7664 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7665 cx.assert_editor_state(indoc! {"
7666 one two
7667 \t\tt[hree
7668 }four"});
7669 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7670 cx.assert_editor_state(indoc! {"
7671 one two
7672 \tt[hree
7673 }four"});
7674 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7675 cx.assert_editor_state(indoc! {"
7676 one two
7677 t[hree
7678 }four"});
7679
7680 // Ensure that indenting/outdenting works when the cursor is at column 0.
7681 cx.set_state(indoc! {"
7682 one two
7683 |three
7684 four"});
7685 cx.assert_editor_state(indoc! {"
7686 one two
7687 |three
7688 four"});
7689 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7690 cx.assert_editor_state(indoc! {"
7691 one two
7692 \t|three
7693 four"});
7694 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7695 cx.assert_editor_state(indoc! {"
7696 one two
7697 |three
7698 four"});
7699 }
7700
7701 #[gpui::test]
7702 fn test_indent_outdent_with_excerpts(cx: &mut gpui::MutableAppContext) {
7703 cx.set_global(
7704 Settings::test(cx)
7705 .with_language_defaults(
7706 "TOML",
7707 EditorSettings {
7708 tab_size: Some(2.try_into().unwrap()),
7709 ..Default::default()
7710 },
7711 )
7712 .with_language_defaults(
7713 "Rust",
7714 EditorSettings {
7715 tab_size: Some(4.try_into().unwrap()),
7716 ..Default::default()
7717 },
7718 ),
7719 );
7720 let toml_language = Arc::new(Language::new(
7721 LanguageConfig {
7722 name: "TOML".into(),
7723 ..Default::default()
7724 },
7725 None,
7726 ));
7727 let rust_language = Arc::new(Language::new(
7728 LanguageConfig {
7729 name: "Rust".into(),
7730 ..Default::default()
7731 },
7732 None,
7733 ));
7734
7735 let toml_buffer = cx
7736 .add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx).with_language(toml_language, cx));
7737 let rust_buffer = cx.add_model(|cx| {
7738 Buffer::new(0, "const c: usize = 3;\n", cx).with_language(rust_language, cx)
7739 });
7740 let multibuffer = cx.add_model(|cx| {
7741 let mut multibuffer = MultiBuffer::new(0);
7742 multibuffer.push_excerpts(
7743 toml_buffer.clone(),
7744 [ExcerptRange {
7745 context: Point::new(0, 0)..Point::new(2, 0),
7746 primary: None,
7747 }],
7748 cx,
7749 );
7750 multibuffer.push_excerpts(
7751 rust_buffer.clone(),
7752 [ExcerptRange {
7753 context: Point::new(0, 0)..Point::new(1, 0),
7754 primary: None,
7755 }],
7756 cx,
7757 );
7758 multibuffer
7759 });
7760
7761 cx.add_window(Default::default(), |cx| {
7762 let mut editor = build_editor(multibuffer, cx);
7763
7764 assert_eq!(
7765 editor.text(cx),
7766 indoc! {"
7767 a = 1
7768 b = 2
7769
7770 const c: usize = 3;
7771 "}
7772 );
7773
7774 select_ranges(
7775 &mut editor,
7776 indoc! {"
7777 [a] = 1
7778 b = 2
7779
7780 [const c:] usize = 3;
7781 "},
7782 cx,
7783 );
7784
7785 editor.tab(&Tab, cx);
7786 assert_text_with_selections(
7787 &mut editor,
7788 indoc! {"
7789 [a] = 1
7790 b = 2
7791
7792 [const c:] usize = 3;
7793 "},
7794 cx,
7795 );
7796 editor.tab_prev(&TabPrev, cx);
7797 assert_text_with_selections(
7798 &mut editor,
7799 indoc! {"
7800 [a] = 1
7801 b = 2
7802
7803 [const c:] usize = 3;
7804 "},
7805 cx,
7806 );
7807
7808 editor
7809 });
7810 }
7811
7812 #[gpui::test]
7813 async fn test_backspace(cx: &mut gpui::TestAppContext) {
7814 let mut cx = EditorTestContext::new(cx).await;
7815 // Basic backspace
7816 cx.set_state(indoc! {"
7817 on|e two three
7818 fou[r} five six
7819 seven {eight nine
7820 ]ten"});
7821 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
7822 cx.assert_editor_state(indoc! {"
7823 o|e two three
7824 fou| five six
7825 seven |ten"});
7826
7827 // Test backspace inside and around indents
7828 cx.set_state(indoc! {"
7829 zero
7830 |one
7831 |two
7832 | | | three
7833 | | four"});
7834 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
7835 cx.assert_editor_state(indoc! {"
7836 zero
7837 |one
7838 |two
7839 | three| four"});
7840
7841 // Test backspace with line_mode set to true
7842 cx.update_editor(|e, _| e.selections.line_mode = true);
7843 cx.set_state(indoc! {"
7844 The |quick |brown
7845 fox jumps over
7846 the lazy dog
7847 |The qu[ick b}rown"});
7848 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
7849 cx.assert_editor_state(indoc! {"
7850 |fox jumps over
7851 the lazy dog|"});
7852 }
7853
7854 #[gpui::test]
7855 async fn test_delete(cx: &mut gpui::TestAppContext) {
7856 let mut cx = EditorTestContext::new(cx).await;
7857
7858 cx.set_state(indoc! {"
7859 on|e two three
7860 fou[r} five six
7861 seven {eight nine
7862 ]ten"});
7863 cx.update_editor(|e, cx| e.delete(&Delete, cx));
7864 cx.assert_editor_state(indoc! {"
7865 on| two three
7866 fou| five six
7867 seven |ten"});
7868
7869 // Test backspace with line_mode set to true
7870 cx.update_editor(|e, _| e.selections.line_mode = true);
7871 cx.set_state(indoc! {"
7872 The |quick |brown
7873 fox {jum]ps over
7874 the lazy dog
7875 |The qu[ick b}rown"});
7876 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
7877 cx.assert_editor_state("|the lazy dog|");
7878 }
7879
7880 #[gpui::test]
7881 fn test_delete_line(cx: &mut gpui::MutableAppContext) {
7882 cx.set_global(Settings::test(cx));
7883 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7884 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7885 view.update(cx, |view, cx| {
7886 view.change_selections(None, cx, |s| {
7887 s.select_display_ranges([
7888 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7889 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
7890 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
7891 ])
7892 });
7893 view.delete_line(&DeleteLine, cx);
7894 assert_eq!(view.display_text(cx), "ghi");
7895 assert_eq!(
7896 view.selections.display_ranges(cx),
7897 vec![
7898 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7899 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
7900 ]
7901 );
7902 });
7903
7904 cx.set_global(Settings::test(cx));
7905 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7906 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7907 view.update(cx, |view, cx| {
7908 view.change_selections(None, cx, |s| {
7909 s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)])
7910 });
7911 view.delete_line(&DeleteLine, cx);
7912 assert_eq!(view.display_text(cx), "ghi\n");
7913 assert_eq!(
7914 view.selections.display_ranges(cx),
7915 vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
7916 );
7917 });
7918 }
7919
7920 #[gpui::test]
7921 fn test_duplicate_line(cx: &mut gpui::MutableAppContext) {
7922 cx.set_global(Settings::test(cx));
7923 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7924 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7925 view.update(cx, |view, cx| {
7926 view.change_selections(None, cx, |s| {
7927 s.select_display_ranges([
7928 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
7929 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7930 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7931 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
7932 ])
7933 });
7934 view.duplicate_line(&DuplicateLine, cx);
7935 assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
7936 assert_eq!(
7937 view.selections.display_ranges(cx),
7938 vec![
7939 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
7940 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7941 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
7942 DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
7943 ]
7944 );
7945 });
7946
7947 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7948 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7949 view.update(cx, |view, cx| {
7950 view.change_selections(None, cx, |s| {
7951 s.select_display_ranges([
7952 DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
7953 DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
7954 ])
7955 });
7956 view.duplicate_line(&DuplicateLine, cx);
7957 assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
7958 assert_eq!(
7959 view.selections.display_ranges(cx),
7960 vec![
7961 DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
7962 DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
7963 ]
7964 );
7965 });
7966 }
7967
7968 #[gpui::test]
7969 fn test_move_line_up_down(cx: &mut gpui::MutableAppContext) {
7970 cx.set_global(Settings::test(cx));
7971 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
7972 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7973 view.update(cx, |view, cx| {
7974 view.fold_ranges(
7975 vec![
7976 Point::new(0, 2)..Point::new(1, 2),
7977 Point::new(2, 3)..Point::new(4, 1),
7978 Point::new(7, 0)..Point::new(8, 4),
7979 ],
7980 cx,
7981 );
7982 view.change_selections(None, cx, |s| {
7983 s.select_display_ranges([
7984 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7985 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
7986 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
7987 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
7988 ])
7989 });
7990 assert_eq!(
7991 view.display_text(cx),
7992 "aa…bbb\nccc…eeee\nfffff\nggggg\n…i\njjjjj"
7993 );
7994
7995 view.move_line_up(&MoveLineUp, cx);
7996 assert_eq!(
7997 view.display_text(cx),
7998 "aa…bbb\nccc…eeee\nggggg\n…i\njjjjj\nfffff"
7999 );
8000 assert_eq!(
8001 view.selections.display_ranges(cx),
8002 vec![
8003 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8004 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8005 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
8006 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
8007 ]
8008 );
8009 });
8010
8011 view.update(cx, |view, cx| {
8012 view.move_line_down(&MoveLineDown, cx);
8013 assert_eq!(
8014 view.display_text(cx),
8015 "ccc…eeee\naa…bbb\nfffff\nggggg\n…i\njjjjj"
8016 );
8017 assert_eq!(
8018 view.selections.display_ranges(cx),
8019 vec![
8020 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8021 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8022 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8023 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
8024 ]
8025 );
8026 });
8027
8028 view.update(cx, |view, cx| {
8029 view.move_line_down(&MoveLineDown, cx);
8030 assert_eq!(
8031 view.display_text(cx),
8032 "ccc…eeee\nfffff\naa…bbb\nggggg\n…i\njjjjj"
8033 );
8034 assert_eq!(
8035 view.selections.display_ranges(cx),
8036 vec![
8037 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8038 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8039 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8040 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
8041 ]
8042 );
8043 });
8044
8045 view.update(cx, |view, cx| {
8046 view.move_line_up(&MoveLineUp, cx);
8047 assert_eq!(
8048 view.display_text(cx),
8049 "ccc…eeee\naa…bbb\nggggg\n…i\njjjjj\nfffff"
8050 );
8051 assert_eq!(
8052 view.selections.display_ranges(cx),
8053 vec![
8054 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8055 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8056 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
8057 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
8058 ]
8059 );
8060 });
8061 }
8062
8063 #[gpui::test]
8064 fn test_move_line_up_down_with_blocks(cx: &mut gpui::MutableAppContext) {
8065 cx.set_global(Settings::test(cx));
8066 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
8067 let snapshot = buffer.read(cx).snapshot(cx);
8068 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8069 editor.update(cx, |editor, cx| {
8070 editor.insert_blocks(
8071 [BlockProperties {
8072 style: BlockStyle::Fixed,
8073 position: snapshot.anchor_after(Point::new(2, 0)),
8074 disposition: BlockDisposition::Below,
8075 height: 1,
8076 render: Arc::new(|_| Empty::new().boxed()),
8077 }],
8078 cx,
8079 );
8080 editor.change_selections(None, cx, |s| {
8081 s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
8082 });
8083 editor.move_line_down(&MoveLineDown, cx);
8084 });
8085 }
8086
8087 #[gpui::test]
8088 fn test_transpose(cx: &mut gpui::MutableAppContext) {
8089 cx.set_global(Settings::test(cx));
8090
8091 cx.add_window(Default::default(), |cx| {
8092 let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
8093
8094 editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
8095 editor.transpose(&Default::default(), cx);
8096 assert_eq!(editor.text(cx), "bac");
8097 assert_eq!(editor.selections.ranges(cx), [2..2]);
8098
8099 editor.transpose(&Default::default(), cx);
8100 assert_eq!(editor.text(cx), "bca");
8101 assert_eq!(editor.selections.ranges(cx), [3..3]);
8102
8103 editor.transpose(&Default::default(), cx);
8104 assert_eq!(editor.text(cx), "bac");
8105 assert_eq!(editor.selections.ranges(cx), [3..3]);
8106
8107 editor
8108 })
8109 .1;
8110
8111 cx.add_window(Default::default(), |cx| {
8112 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
8113
8114 editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
8115 editor.transpose(&Default::default(), cx);
8116 assert_eq!(editor.text(cx), "acb\nde");
8117 assert_eq!(editor.selections.ranges(cx), [3..3]);
8118
8119 editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
8120 editor.transpose(&Default::default(), cx);
8121 assert_eq!(editor.text(cx), "acbd\ne");
8122 assert_eq!(editor.selections.ranges(cx), [5..5]);
8123
8124 editor.transpose(&Default::default(), cx);
8125 assert_eq!(editor.text(cx), "acbde\n");
8126 assert_eq!(editor.selections.ranges(cx), [6..6]);
8127
8128 editor.transpose(&Default::default(), cx);
8129 assert_eq!(editor.text(cx), "acbd\ne");
8130 assert_eq!(editor.selections.ranges(cx), [6..6]);
8131
8132 editor
8133 })
8134 .1;
8135
8136 cx.add_window(Default::default(), |cx| {
8137 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
8138
8139 editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
8140 editor.transpose(&Default::default(), cx);
8141 assert_eq!(editor.text(cx), "bacd\ne");
8142 assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
8143
8144 editor.transpose(&Default::default(), cx);
8145 assert_eq!(editor.text(cx), "bcade\n");
8146 assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
8147
8148 editor.transpose(&Default::default(), cx);
8149 assert_eq!(editor.text(cx), "bcda\ne");
8150 assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
8151
8152 editor.transpose(&Default::default(), cx);
8153 assert_eq!(editor.text(cx), "bcade\n");
8154 assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
8155
8156 editor.transpose(&Default::default(), cx);
8157 assert_eq!(editor.text(cx), "bcaed\n");
8158 assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
8159
8160 editor
8161 })
8162 .1;
8163
8164 cx.add_window(Default::default(), |cx| {
8165 let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
8166
8167 editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
8168 editor.transpose(&Default::default(), cx);
8169 assert_eq!(editor.text(cx), "🏀🍐✋");
8170 assert_eq!(editor.selections.ranges(cx), [8..8]);
8171
8172 editor.transpose(&Default::default(), cx);
8173 assert_eq!(editor.text(cx), "🏀✋🍐");
8174 assert_eq!(editor.selections.ranges(cx), [11..11]);
8175
8176 editor.transpose(&Default::default(), cx);
8177 assert_eq!(editor.text(cx), "🏀🍐✋");
8178 assert_eq!(editor.selections.ranges(cx), [11..11]);
8179
8180 editor
8181 })
8182 .1;
8183 }
8184
8185 #[gpui::test]
8186 async fn test_clipboard(cx: &mut gpui::TestAppContext) {
8187 let mut cx = EditorTestContext::new(cx).await;
8188
8189 cx.set_state("[one✅ }two [three }four [five }six ");
8190 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8191 cx.assert_editor_state("|two |four |six ");
8192
8193 // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
8194 cx.set_state("two |four |six |");
8195 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8196 cx.assert_editor_state("two one✅ |four three |six five |");
8197
8198 // Paste again but with only two cursors. Since the number of cursors doesn't
8199 // match the number of slices in the clipboard, the entire clipboard text
8200 // is pasted at each cursor.
8201 cx.set_state("|two one✅ four three six five |");
8202 cx.update_editor(|e, cx| {
8203 e.handle_input(&Input("( ".into()), cx);
8204 e.paste(&Paste, cx);
8205 e.handle_input(&Input(") ".into()), cx);
8206 });
8207 cx.assert_editor_state(indoc! {"
8208 ( one✅
8209 three
8210 five ) |two one✅ four three six five ( one✅
8211 three
8212 five ) |"});
8213
8214 // Cut with three selections, one of which is full-line.
8215 cx.set_state(indoc! {"
8216 1[2}3
8217 4|567
8218 [8}9"});
8219 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8220 cx.assert_editor_state(indoc! {"
8221 1|3
8222 |9"});
8223
8224 // Paste with three selections, noticing how the copied selection that was full-line
8225 // gets inserted before the second cursor.
8226 cx.set_state(indoc! {"
8227 1|3
8228 9|
8229 [o}ne"});
8230 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8231 cx.assert_editor_state(indoc! {"
8232 12|3
8233 4567
8234 9|
8235 8|ne"});
8236
8237 // Copy with a single cursor only, which writes the whole line into the clipboard.
8238 cx.set_state(indoc! {"
8239 The quick brown
8240 fox ju|mps over
8241 the lazy dog"});
8242 cx.update_editor(|e, cx| e.copy(&Copy, cx));
8243 cx.cx.assert_clipboard_content(Some("fox jumps over\n"));
8244
8245 // Paste with three selections, noticing how the copied full-line selection is inserted
8246 // before the empty selections but replaces the selection that is non-empty.
8247 cx.set_state(indoc! {"
8248 T|he quick brown
8249 [fo}x jumps over
8250 t|he lazy dog"});
8251 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8252 cx.assert_editor_state(indoc! {"
8253 fox jumps over
8254 T|he quick brown
8255 fox jumps over
8256 |x jumps over
8257 fox jumps over
8258 t|he lazy dog"});
8259 }
8260
8261 #[gpui::test]
8262 fn test_select_all(cx: &mut gpui::MutableAppContext) {
8263 cx.set_global(Settings::test(cx));
8264 let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
8265 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8266 view.update(cx, |view, cx| {
8267 view.select_all(&SelectAll, cx);
8268 assert_eq!(
8269 view.selections.display_ranges(cx),
8270 &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
8271 );
8272 });
8273 }
8274
8275 #[gpui::test]
8276 fn test_select_line(cx: &mut gpui::MutableAppContext) {
8277 cx.set_global(Settings::test(cx));
8278 let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
8279 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8280 view.update(cx, |view, cx| {
8281 view.change_selections(None, cx, |s| {
8282 s.select_display_ranges([
8283 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8284 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8285 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8286 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
8287 ])
8288 });
8289 view.select_line(&SelectLine, cx);
8290 assert_eq!(
8291 view.selections.display_ranges(cx),
8292 vec![
8293 DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
8294 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
8295 ]
8296 );
8297 });
8298
8299 view.update(cx, |view, cx| {
8300 view.select_line(&SelectLine, cx);
8301 assert_eq!(
8302 view.selections.display_ranges(cx),
8303 vec![
8304 DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
8305 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
8306 ]
8307 );
8308 });
8309
8310 view.update(cx, |view, cx| {
8311 view.select_line(&SelectLine, cx);
8312 assert_eq!(
8313 view.selections.display_ranges(cx),
8314 vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
8315 );
8316 });
8317 }
8318
8319 #[gpui::test]
8320 fn test_split_selection_into_lines(cx: &mut gpui::MutableAppContext) {
8321 cx.set_global(Settings::test(cx));
8322 let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
8323 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8324 view.update(cx, |view, cx| {
8325 view.fold_ranges(
8326 vec![
8327 Point::new(0, 2)..Point::new(1, 2),
8328 Point::new(2, 3)..Point::new(4, 1),
8329 Point::new(7, 0)..Point::new(8, 4),
8330 ],
8331 cx,
8332 );
8333 view.change_selections(None, cx, |s| {
8334 s.select_display_ranges([
8335 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8336 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8337 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8338 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
8339 ])
8340 });
8341 assert_eq!(view.display_text(cx), "aa…bbb\nccc…eeee\nfffff\nggggg\n…i");
8342 });
8343
8344 view.update(cx, |view, cx| {
8345 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
8346 assert_eq!(
8347 view.display_text(cx),
8348 "aaaaa\nbbbbb\nccc…eeee\nfffff\nggggg\n…i"
8349 );
8350 assert_eq!(
8351 view.selections.display_ranges(cx),
8352 [
8353 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8354 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8355 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
8356 DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
8357 ]
8358 );
8359 });
8360
8361 view.update(cx, |view, cx| {
8362 view.change_selections(None, cx, |s| {
8363 s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
8364 });
8365 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
8366 assert_eq!(
8367 view.display_text(cx),
8368 "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
8369 );
8370 assert_eq!(
8371 view.selections.display_ranges(cx),
8372 [
8373 DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
8374 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
8375 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
8376 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
8377 DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
8378 DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
8379 DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
8380 DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
8381 ]
8382 );
8383 });
8384 }
8385
8386 #[gpui::test]
8387 fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) {
8388 cx.set_global(Settings::test(cx));
8389 let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
8390 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8391
8392 view.update(cx, |view, cx| {
8393 view.change_selections(None, cx, |s| {
8394 s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)])
8395 });
8396 });
8397 view.update(cx, |view, cx| {
8398 view.add_selection_above(&AddSelectionAbove, cx);
8399 assert_eq!(
8400 view.selections.display_ranges(cx),
8401 vec![
8402 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8403 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8404 ]
8405 );
8406 });
8407
8408 view.update(cx, |view, cx| {
8409 view.add_selection_above(&AddSelectionAbove, cx);
8410 assert_eq!(
8411 view.selections.display_ranges(cx),
8412 vec![
8413 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8414 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8415 ]
8416 );
8417 });
8418
8419 view.update(cx, |view, cx| {
8420 view.add_selection_below(&AddSelectionBelow, cx);
8421 assert_eq!(
8422 view.selections.display_ranges(cx),
8423 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
8424 );
8425
8426 view.undo_selection(&UndoSelection, cx);
8427 assert_eq!(
8428 view.selections.display_ranges(cx),
8429 vec![
8430 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8431 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8432 ]
8433 );
8434
8435 view.redo_selection(&RedoSelection, cx);
8436 assert_eq!(
8437 view.selections.display_ranges(cx),
8438 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
8439 );
8440 });
8441
8442 view.update(cx, |view, cx| {
8443 view.add_selection_below(&AddSelectionBelow, cx);
8444 assert_eq!(
8445 view.selections.display_ranges(cx),
8446 vec![
8447 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
8448 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
8449 ]
8450 );
8451 });
8452
8453 view.update(cx, |view, cx| {
8454 view.add_selection_below(&AddSelectionBelow, cx);
8455 assert_eq!(
8456 view.selections.display_ranges(cx),
8457 vec![
8458 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
8459 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
8460 ]
8461 );
8462 });
8463
8464 view.update(cx, |view, cx| {
8465 view.change_selections(None, cx, |s| {
8466 s.select_display_ranges([DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)])
8467 });
8468 });
8469 view.update(cx, |view, cx| {
8470 view.add_selection_below(&AddSelectionBelow, cx);
8471 assert_eq!(
8472 view.selections.display_ranges(cx),
8473 vec![
8474 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
8475 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
8476 ]
8477 );
8478 });
8479
8480 view.update(cx, |view, cx| {
8481 view.add_selection_below(&AddSelectionBelow, cx);
8482 assert_eq!(
8483 view.selections.display_ranges(cx),
8484 vec![
8485 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
8486 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
8487 ]
8488 );
8489 });
8490
8491 view.update(cx, |view, cx| {
8492 view.add_selection_above(&AddSelectionAbove, cx);
8493 assert_eq!(
8494 view.selections.display_ranges(cx),
8495 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
8496 );
8497 });
8498
8499 view.update(cx, |view, cx| {
8500 view.add_selection_above(&AddSelectionAbove, cx);
8501 assert_eq!(
8502 view.selections.display_ranges(cx),
8503 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
8504 );
8505 });
8506
8507 view.update(cx, |view, cx| {
8508 view.change_selections(None, cx, |s| {
8509 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)])
8510 });
8511 view.add_selection_below(&AddSelectionBelow, cx);
8512 assert_eq!(
8513 view.selections.display_ranges(cx),
8514 vec![
8515 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8516 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8517 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8518 ]
8519 );
8520 });
8521
8522 view.update(cx, |view, cx| {
8523 view.add_selection_below(&AddSelectionBelow, cx);
8524 assert_eq!(
8525 view.selections.display_ranges(cx),
8526 vec![
8527 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8528 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8529 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8530 DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
8531 ]
8532 );
8533 });
8534
8535 view.update(cx, |view, cx| {
8536 view.add_selection_above(&AddSelectionAbove, cx);
8537 assert_eq!(
8538 view.selections.display_ranges(cx),
8539 vec![
8540 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8541 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8542 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8543 ]
8544 );
8545 });
8546
8547 view.update(cx, |view, cx| {
8548 view.change_selections(None, cx, |s| {
8549 s.select_display_ranges([DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)])
8550 });
8551 });
8552 view.update(cx, |view, cx| {
8553 view.add_selection_above(&AddSelectionAbove, cx);
8554 assert_eq!(
8555 view.selections.display_ranges(cx),
8556 vec![
8557 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
8558 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
8559 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
8560 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
8561 ]
8562 );
8563 });
8564
8565 view.update(cx, |view, cx| {
8566 view.add_selection_below(&AddSelectionBelow, cx);
8567 assert_eq!(
8568 view.selections.display_ranges(cx),
8569 vec![
8570 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
8571 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
8572 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
8573 ]
8574 );
8575 });
8576 }
8577
8578 #[gpui::test]
8579 fn test_select_next(cx: &mut gpui::MutableAppContext) {
8580 cx.set_global(Settings::test(cx));
8581
8582 let (text, ranges) = marked_text_ranges("[abc]\n[abc] [abc]\ndefabc\n[abc]");
8583 let buffer = MultiBuffer::build_simple(&text, cx);
8584 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8585
8586 view.update(cx, |view, cx| {
8587 view.change_selections(None, cx, |s| {
8588 s.select_ranges([ranges[1].start + 1..ranges[1].start + 1])
8589 });
8590 view.select_next(
8591 &SelectNext {
8592 replace_newest: false,
8593 },
8594 cx,
8595 );
8596 assert_eq!(view.selections.ranges(cx), &ranges[1..2]);
8597
8598 view.select_next(
8599 &SelectNext {
8600 replace_newest: false,
8601 },
8602 cx,
8603 );
8604 assert_eq!(view.selections.ranges(cx), &ranges[1..3]);
8605
8606 view.undo_selection(&UndoSelection, cx);
8607 assert_eq!(view.selections.ranges(cx), &ranges[1..2]);
8608
8609 view.redo_selection(&RedoSelection, cx);
8610 assert_eq!(view.selections.ranges(cx), &ranges[1..3]);
8611
8612 view.select_next(
8613 &SelectNext {
8614 replace_newest: false,
8615 },
8616 cx,
8617 );
8618 assert_eq!(view.selections.ranges(cx), &ranges[1..4]);
8619
8620 view.select_next(
8621 &SelectNext {
8622 replace_newest: false,
8623 },
8624 cx,
8625 );
8626 assert_eq!(view.selections.ranges(cx), &ranges[0..4]);
8627 });
8628 }
8629
8630 #[gpui::test]
8631 async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
8632 cx.update(|cx| cx.set_global(Settings::test(cx)));
8633 let language = Arc::new(Language::new(
8634 LanguageConfig::default(),
8635 Some(tree_sitter_rust::language()),
8636 ));
8637
8638 let text = r#"
8639 use mod1::mod2::{mod3, mod4};
8640
8641 fn fn_1(param1: bool, param2: &str) {
8642 let var1 = "text";
8643 }
8644 "#
8645 .unindent();
8646
8647 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8648 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8649 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
8650 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
8651 .await;
8652
8653 view.update(cx, |view, cx| {
8654 view.change_selections(None, cx, |s| {
8655 s.select_display_ranges([
8656 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8657 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8658 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8659 ]);
8660 });
8661 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8662 });
8663 assert_eq!(
8664 view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
8665 &[
8666 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
8667 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8668 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
8669 ]
8670 );
8671
8672 view.update(cx, |view, cx| {
8673 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8674 });
8675 assert_eq!(
8676 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8677 &[
8678 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
8679 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
8680 ]
8681 );
8682
8683 view.update(cx, |view, cx| {
8684 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8685 });
8686 assert_eq!(
8687 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8688 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
8689 );
8690
8691 // Trying to expand the selected syntax node one more time has no effect.
8692 view.update(cx, |view, cx| {
8693 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8694 });
8695 assert_eq!(
8696 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8697 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
8698 );
8699
8700 view.update(cx, |view, cx| {
8701 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8702 });
8703 assert_eq!(
8704 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8705 &[
8706 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
8707 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
8708 ]
8709 );
8710
8711 view.update(cx, |view, cx| {
8712 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8713 });
8714 assert_eq!(
8715 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8716 &[
8717 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
8718 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8719 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
8720 ]
8721 );
8722
8723 view.update(cx, |view, cx| {
8724 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8725 });
8726 assert_eq!(
8727 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8728 &[
8729 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8730 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8731 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8732 ]
8733 );
8734
8735 // Trying to shrink the selected syntax node one more time has no effect.
8736 view.update(cx, |view, cx| {
8737 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8738 });
8739 assert_eq!(
8740 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8741 &[
8742 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8743 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8744 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8745 ]
8746 );
8747
8748 // Ensure that we keep expanding the selection if the larger selection starts or ends within
8749 // a fold.
8750 view.update(cx, |view, cx| {
8751 view.fold_ranges(
8752 vec![
8753 Point::new(0, 21)..Point::new(0, 24),
8754 Point::new(3, 20)..Point::new(3, 22),
8755 ],
8756 cx,
8757 );
8758 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8759 });
8760 assert_eq!(
8761 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8762 &[
8763 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
8764 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8765 DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
8766 ]
8767 );
8768 }
8769
8770 #[gpui::test]
8771 async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
8772 cx.update(|cx| cx.set_global(Settings::test(cx)));
8773 let language = Arc::new(
8774 Language::new(
8775 LanguageConfig {
8776 brackets: vec![
8777 BracketPair {
8778 start: "{".to_string(),
8779 end: "}".to_string(),
8780 close: false,
8781 newline: true,
8782 },
8783 BracketPair {
8784 start: "(".to_string(),
8785 end: ")".to_string(),
8786 close: false,
8787 newline: true,
8788 },
8789 ],
8790 ..Default::default()
8791 },
8792 Some(tree_sitter_rust::language()),
8793 )
8794 .with_indents_query(
8795 r#"
8796 (_ "(" ")" @end) @indent
8797 (_ "{" "}" @end) @indent
8798 "#,
8799 )
8800 .unwrap(),
8801 );
8802
8803 let text = "fn a() {}";
8804
8805 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8806 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8807 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
8808 editor
8809 .condition(&cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
8810 .await;
8811
8812 editor.update(cx, |editor, cx| {
8813 editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
8814 editor.newline(&Newline, cx);
8815 assert_eq!(editor.text(cx), "fn a(\n \n) {\n \n}\n");
8816 assert_eq!(
8817 editor.selections.ranges(cx),
8818 &[
8819 Point::new(1, 4)..Point::new(1, 4),
8820 Point::new(3, 4)..Point::new(3, 4),
8821 Point::new(5, 0)..Point::new(5, 0)
8822 ]
8823 );
8824 });
8825 }
8826
8827 #[gpui::test]
8828 async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
8829 cx.update(|cx| cx.set_global(Settings::test(cx)));
8830 let language = Arc::new(Language::new(
8831 LanguageConfig {
8832 brackets: vec![
8833 BracketPair {
8834 start: "{".to_string(),
8835 end: "}".to_string(),
8836 close: true,
8837 newline: true,
8838 },
8839 BracketPair {
8840 start: "/*".to_string(),
8841 end: " */".to_string(),
8842 close: true,
8843 newline: true,
8844 },
8845 BracketPair {
8846 start: "[".to_string(),
8847 end: "]".to_string(),
8848 close: false,
8849 newline: true,
8850 },
8851 ],
8852 autoclose_before: "})]".to_string(),
8853 ..Default::default()
8854 },
8855 Some(tree_sitter_rust::language()),
8856 ));
8857
8858 let text = r#"
8859 a
8860
8861 /
8862
8863 "#
8864 .unindent();
8865
8866 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8867 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8868 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
8869 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
8870 .await;
8871
8872 view.update(cx, |view, cx| {
8873 view.change_selections(None, cx, |s| {
8874 s.select_display_ranges([
8875 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8876 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8877 ])
8878 });
8879
8880 view.handle_input(&Input("{".to_string()), cx);
8881 view.handle_input(&Input("{".to_string()), cx);
8882 view.handle_input(&Input("{".to_string()), cx);
8883 assert_eq!(
8884 view.text(cx),
8885 "
8886 {{{}}}
8887 {{{}}}
8888 /
8889
8890 "
8891 .unindent()
8892 );
8893
8894 view.move_right(&MoveRight, cx);
8895 view.handle_input(&Input("}".to_string()), cx);
8896 view.handle_input(&Input("}".to_string()), cx);
8897 view.handle_input(&Input("}".to_string()), cx);
8898 assert_eq!(
8899 view.text(cx),
8900 "
8901 {{{}}}}
8902 {{{}}}}
8903 /
8904
8905 "
8906 .unindent()
8907 );
8908
8909 view.undo(&Undo, cx);
8910 view.handle_input(&Input("/".to_string()), cx);
8911 view.handle_input(&Input("*".to_string()), cx);
8912 assert_eq!(
8913 view.text(cx),
8914 "
8915 /* */
8916 /* */
8917 /
8918
8919 "
8920 .unindent()
8921 );
8922
8923 view.undo(&Undo, cx);
8924 view.change_selections(None, cx, |s| {
8925 s.select_display_ranges([
8926 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8927 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8928 ])
8929 });
8930 view.handle_input(&Input("*".to_string()), cx);
8931 assert_eq!(
8932 view.text(cx),
8933 "
8934 a
8935
8936 /*
8937 *
8938 "
8939 .unindent()
8940 );
8941
8942 // Don't autoclose if the next character isn't whitespace and isn't
8943 // listed in the language's "autoclose_before" section.
8944 view.finalize_last_transaction(cx);
8945 view.change_selections(None, cx, |s| {
8946 s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)])
8947 });
8948 view.handle_input(&Input("{".to_string()), cx);
8949 assert_eq!(
8950 view.text(cx),
8951 "
8952 {a
8953
8954 /*
8955 *
8956 "
8957 .unindent()
8958 );
8959
8960 view.undo(&Undo, cx);
8961 view.change_selections(None, cx, |s| {
8962 s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1)])
8963 });
8964 view.handle_input(&Input("{".to_string()), cx);
8965 assert_eq!(
8966 view.text(cx),
8967 "
8968 {a}
8969
8970 /*
8971 *
8972 "
8973 .unindent()
8974 );
8975 assert_eq!(
8976 view.selections.display_ranges(cx),
8977 [DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]
8978 );
8979
8980 view.undo(&Undo, cx);
8981 view.handle_input(&Input("[".to_string()), cx);
8982 assert_eq!(
8983 view.text(cx),
8984 "
8985 [a]
8986
8987 /*
8988 *
8989 "
8990 .unindent()
8991 );
8992 assert_eq!(
8993 view.selections.display_ranges(cx),
8994 [DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]
8995 );
8996
8997 view.undo(&Undo, cx);
8998 view.change_selections(None, cx, |s| {
8999 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)])
9000 });
9001 view.handle_input(&Input("[".to_string()), cx);
9002 assert_eq!(
9003 view.text(cx),
9004 "
9005 a[
9006
9007 /*
9008 *
9009 "
9010 .unindent()
9011 );
9012 assert_eq!(
9013 view.selections.display_ranges(cx),
9014 [DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2)]
9015 );
9016 });
9017 }
9018
9019 #[gpui::test]
9020 async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
9021 cx.update(|cx| cx.set_global(Settings::test(cx)));
9022 let language = Arc::new(Language::new(
9023 LanguageConfig {
9024 brackets: vec![BracketPair {
9025 start: "{".to_string(),
9026 end: "}".to_string(),
9027 close: true,
9028 newline: true,
9029 }],
9030 ..Default::default()
9031 },
9032 Some(tree_sitter_rust::language()),
9033 ));
9034
9035 let text = r#"
9036 a
9037 b
9038 c
9039 "#
9040 .unindent();
9041
9042 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9043 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9044 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9045 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9046 .await;
9047
9048 view.update(cx, |view, cx| {
9049 view.change_selections(None, cx, |s| {
9050 s.select_display_ranges([
9051 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9052 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
9053 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
9054 ])
9055 });
9056
9057 view.handle_input(&Input("{".to_string()), cx);
9058 view.handle_input(&Input("{".to_string()), cx);
9059 view.handle_input(&Input("{".to_string()), cx);
9060 assert_eq!(
9061 view.text(cx),
9062 "
9063 {{{a}}}
9064 {{{b}}}
9065 {{{c}}}
9066 "
9067 .unindent()
9068 );
9069 assert_eq!(
9070 view.selections.display_ranges(cx),
9071 [
9072 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
9073 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
9074 DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
9075 ]
9076 );
9077
9078 view.undo(&Undo, cx);
9079 assert_eq!(
9080 view.text(cx),
9081 "
9082 a
9083 b
9084 c
9085 "
9086 .unindent()
9087 );
9088 assert_eq!(
9089 view.selections.display_ranges(cx),
9090 [
9091 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9092 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
9093 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
9094 ]
9095 );
9096 });
9097 }
9098
9099 #[gpui::test]
9100 async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
9101 cx.update(|cx| cx.set_global(Settings::test(cx)));
9102 let language = Arc::new(Language::new(
9103 LanguageConfig {
9104 brackets: vec![BracketPair {
9105 start: "{".to_string(),
9106 end: "}".to_string(),
9107 close: true,
9108 newline: true,
9109 }],
9110 autoclose_before: "}".to_string(),
9111 ..Default::default()
9112 },
9113 Some(tree_sitter_rust::language()),
9114 ));
9115
9116 let text = r#"
9117 a
9118 b
9119 c
9120 "#
9121 .unindent();
9122
9123 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9124 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9125 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9126 editor
9127 .condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9128 .await;
9129
9130 editor.update(cx, |editor, cx| {
9131 editor.change_selections(None, cx, |s| {
9132 s.select_ranges([
9133 Point::new(0, 1)..Point::new(0, 1),
9134 Point::new(1, 1)..Point::new(1, 1),
9135 Point::new(2, 1)..Point::new(2, 1),
9136 ])
9137 });
9138
9139 editor.handle_input(&Input("{".to_string()), cx);
9140 editor.handle_input(&Input("{".to_string()), cx);
9141 editor.handle_input(&Input("_".to_string()), cx);
9142 assert_eq!(
9143 editor.text(cx),
9144 "
9145 a{{_}}
9146 b{{_}}
9147 c{{_}}
9148 "
9149 .unindent()
9150 );
9151 assert_eq!(
9152 editor.selections.ranges::<Point>(cx),
9153 [
9154 Point::new(0, 4)..Point::new(0, 4),
9155 Point::new(1, 4)..Point::new(1, 4),
9156 Point::new(2, 4)..Point::new(2, 4)
9157 ]
9158 );
9159
9160 editor.backspace(&Default::default(), cx);
9161 editor.backspace(&Default::default(), cx);
9162 assert_eq!(
9163 editor.text(cx),
9164 "
9165 a{}
9166 b{}
9167 c{}
9168 "
9169 .unindent()
9170 );
9171 assert_eq!(
9172 editor.selections.ranges::<Point>(cx),
9173 [
9174 Point::new(0, 2)..Point::new(0, 2),
9175 Point::new(1, 2)..Point::new(1, 2),
9176 Point::new(2, 2)..Point::new(2, 2)
9177 ]
9178 );
9179
9180 editor.delete_to_previous_word_start(&Default::default(), cx);
9181 assert_eq!(
9182 editor.text(cx),
9183 "
9184 a
9185 b
9186 c
9187 "
9188 .unindent()
9189 );
9190 assert_eq!(
9191 editor.selections.ranges::<Point>(cx),
9192 [
9193 Point::new(0, 1)..Point::new(0, 1),
9194 Point::new(1, 1)..Point::new(1, 1),
9195 Point::new(2, 1)..Point::new(2, 1)
9196 ]
9197 );
9198 });
9199 }
9200
9201 #[gpui::test]
9202 async fn test_snippets(cx: &mut gpui::TestAppContext) {
9203 cx.update(|cx| cx.set_global(Settings::test(cx)));
9204
9205 let (text, insertion_ranges) = marked_text_ranges(indoc! {"
9206 a.| b
9207 a.| b
9208 a.| b"});
9209 let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
9210 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9211
9212 editor.update(cx, |editor, cx| {
9213 let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
9214
9215 editor
9216 .insert_snippet(&insertion_ranges, snippet, cx)
9217 .unwrap();
9218
9219 fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text_ranges: &str) {
9220 let range_markers = ('<', '>');
9221 let (expected_text, mut selection_ranges_lookup) =
9222 marked_text_ranges_by(marked_text_ranges, vec![range_markers.clone().into()]);
9223 let selection_ranges = selection_ranges_lookup
9224 .remove(&range_markers.into())
9225 .unwrap();
9226 assert_eq!(editor.text(cx), expected_text);
9227 assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
9228 }
9229 assert(
9230 editor,
9231 cx,
9232 indoc! {"
9233 a.f(<one>, two, <three>) b
9234 a.f(<one>, two, <three>) b
9235 a.f(<one>, two, <three>) b"},
9236 );
9237
9238 // Can't move earlier than the first tab stop
9239 assert!(!editor.move_to_prev_snippet_tabstop(cx));
9240 assert(
9241 editor,
9242 cx,
9243 indoc! {"
9244 a.f(<one>, two, <three>) b
9245 a.f(<one>, two, <three>) b
9246 a.f(<one>, two, <three>) b"},
9247 );
9248
9249 assert!(editor.move_to_next_snippet_tabstop(cx));
9250 assert(
9251 editor,
9252 cx,
9253 indoc! {"
9254 a.f(one, <two>, three) b
9255 a.f(one, <two>, three) b
9256 a.f(one, <two>, three) b"},
9257 );
9258
9259 editor.move_to_prev_snippet_tabstop(cx);
9260 assert(
9261 editor,
9262 cx,
9263 indoc! {"
9264 a.f(<one>, two, <three>) b
9265 a.f(<one>, two, <three>) b
9266 a.f(<one>, two, <three>) b"},
9267 );
9268
9269 assert!(editor.move_to_next_snippet_tabstop(cx));
9270 assert(
9271 editor,
9272 cx,
9273 indoc! {"
9274 a.f(one, <two>, three) b
9275 a.f(one, <two>, three) b
9276 a.f(one, <two>, three) b"},
9277 );
9278 assert!(editor.move_to_next_snippet_tabstop(cx));
9279 assert(
9280 editor,
9281 cx,
9282 indoc! {"
9283 a.f(one, two, three)<> b
9284 a.f(one, two, three)<> b
9285 a.f(one, two, three)<> b"},
9286 );
9287
9288 // As soon as the last tab stop is reached, snippet state is gone
9289 editor.move_to_prev_snippet_tabstop(cx);
9290 assert(
9291 editor,
9292 cx,
9293 indoc! {"
9294 a.f(one, two, three)<> b
9295 a.f(one, two, three)<> b
9296 a.f(one, two, three)<> b"},
9297 );
9298 });
9299 }
9300
9301 #[gpui::test]
9302 async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
9303 cx.foreground().forbid_parking();
9304
9305 let mut language = Language::new(
9306 LanguageConfig {
9307 name: "Rust".into(),
9308 path_suffixes: vec!["rs".to_string()],
9309 ..Default::default()
9310 },
9311 Some(tree_sitter_rust::language()),
9312 );
9313 let mut fake_servers = language
9314 .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
9315 capabilities: lsp::ServerCapabilities {
9316 document_formatting_provider: Some(lsp::OneOf::Left(true)),
9317 ..Default::default()
9318 },
9319 ..Default::default()
9320 }))
9321 .await;
9322
9323 let fs = FakeFs::new(cx.background().clone());
9324 fs.insert_file("/file.rs", Default::default()).await;
9325
9326 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
9327 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
9328 let buffer = project
9329 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
9330 .await
9331 .unwrap();
9332
9333 cx.foreground().start_waiting();
9334 let fake_server = fake_servers.next().await.unwrap();
9335
9336 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9337 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9338 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9339 assert!(cx.read(|cx| editor.is_dirty(cx)));
9340
9341 let save = cx.update(|cx| editor.save(project.clone(), cx));
9342 fake_server
9343 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9344 assert_eq!(
9345 params.text_document.uri,
9346 lsp::Url::from_file_path("/file.rs").unwrap()
9347 );
9348 assert_eq!(params.options.tab_size, 4);
9349 Ok(Some(vec![lsp::TextEdit::new(
9350 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
9351 ", ".to_string(),
9352 )]))
9353 })
9354 .next()
9355 .await;
9356 cx.foreground().start_waiting();
9357 save.await.unwrap();
9358 assert_eq!(
9359 editor.read_with(cx, |editor, cx| editor.text(cx)),
9360 "one, two\nthree\n"
9361 );
9362 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9363
9364 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9365 assert!(cx.read(|cx| editor.is_dirty(cx)));
9366
9367 // Ensure we can still save even if formatting hangs.
9368 fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9369 assert_eq!(
9370 params.text_document.uri,
9371 lsp::Url::from_file_path("/file.rs").unwrap()
9372 );
9373 futures::future::pending::<()>().await;
9374 unreachable!()
9375 });
9376 let save = cx.update(|cx| editor.save(project.clone(), cx));
9377 cx.foreground().advance_clock(items::FORMAT_TIMEOUT);
9378 cx.foreground().start_waiting();
9379 save.await.unwrap();
9380 assert_eq!(
9381 editor.read_with(cx, |editor, cx| editor.text(cx)),
9382 "one\ntwo\nthree\n"
9383 );
9384 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9385
9386 // Set rust language override and assert overriden tabsize is sent to language server
9387 cx.update(|cx| {
9388 cx.update_global::<Settings, _, _>(|settings, _| {
9389 settings.language_overrides.insert(
9390 "Rust".into(),
9391 EditorSettings {
9392 tab_size: Some(8.try_into().unwrap()),
9393 ..Default::default()
9394 },
9395 );
9396 })
9397 });
9398
9399 let save = cx.update(|cx| editor.save(project.clone(), cx));
9400 fake_server
9401 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9402 assert_eq!(
9403 params.text_document.uri,
9404 lsp::Url::from_file_path("/file.rs").unwrap()
9405 );
9406 assert_eq!(params.options.tab_size, 8);
9407 Ok(Some(vec![]))
9408 })
9409 .next()
9410 .await;
9411 cx.foreground().start_waiting();
9412 save.await.unwrap();
9413 }
9414
9415 #[gpui::test]
9416 async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
9417 cx.foreground().forbid_parking();
9418
9419 let mut language = Language::new(
9420 LanguageConfig {
9421 name: "Rust".into(),
9422 path_suffixes: vec!["rs".to_string()],
9423 ..Default::default()
9424 },
9425 Some(tree_sitter_rust::language()),
9426 );
9427 let mut fake_servers = language
9428 .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
9429 capabilities: lsp::ServerCapabilities {
9430 document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
9431 ..Default::default()
9432 },
9433 ..Default::default()
9434 }))
9435 .await;
9436
9437 let fs = FakeFs::new(cx.background().clone());
9438 fs.insert_file("/file.rs", Default::default()).await;
9439
9440 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
9441 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
9442 let buffer = project
9443 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
9444 .await
9445 .unwrap();
9446
9447 cx.foreground().start_waiting();
9448 let fake_server = fake_servers.next().await.unwrap();
9449
9450 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9451 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9452 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9453 assert!(cx.read(|cx| editor.is_dirty(cx)));
9454
9455 let save = cx.update(|cx| editor.save(project.clone(), cx));
9456 fake_server
9457 .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
9458 assert_eq!(
9459 params.text_document.uri,
9460 lsp::Url::from_file_path("/file.rs").unwrap()
9461 );
9462 assert_eq!(params.options.tab_size, 4);
9463 Ok(Some(vec![lsp::TextEdit::new(
9464 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
9465 ", ".to_string(),
9466 )]))
9467 })
9468 .next()
9469 .await;
9470 cx.foreground().start_waiting();
9471 save.await.unwrap();
9472 assert_eq!(
9473 editor.read_with(cx, |editor, cx| editor.text(cx)),
9474 "one, two\nthree\n"
9475 );
9476 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9477
9478 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9479 assert!(cx.read(|cx| editor.is_dirty(cx)));
9480
9481 // Ensure we can still save even if formatting hangs.
9482 fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
9483 move |params, _| async move {
9484 assert_eq!(
9485 params.text_document.uri,
9486 lsp::Url::from_file_path("/file.rs").unwrap()
9487 );
9488 futures::future::pending::<()>().await;
9489 unreachable!()
9490 },
9491 );
9492 let save = cx.update(|cx| editor.save(project.clone(), cx));
9493 cx.foreground().advance_clock(items::FORMAT_TIMEOUT);
9494 cx.foreground().start_waiting();
9495 save.await.unwrap();
9496 assert_eq!(
9497 editor.read_with(cx, |editor, cx| editor.text(cx)),
9498 "one\ntwo\nthree\n"
9499 );
9500 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9501
9502 // Set rust language override and assert overriden tabsize is sent to language server
9503 cx.update(|cx| {
9504 cx.update_global::<Settings, _, _>(|settings, _| {
9505 settings.language_overrides.insert(
9506 "Rust".into(),
9507 EditorSettings {
9508 tab_size: Some(8.try_into().unwrap()),
9509 ..Default::default()
9510 },
9511 );
9512 })
9513 });
9514
9515 let save = cx.update(|cx| editor.save(project.clone(), cx));
9516 fake_server
9517 .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
9518 assert_eq!(
9519 params.text_document.uri,
9520 lsp::Url::from_file_path("/file.rs").unwrap()
9521 );
9522 assert_eq!(params.options.tab_size, 8);
9523 Ok(Some(vec![]))
9524 })
9525 .next()
9526 .await;
9527 cx.foreground().start_waiting();
9528 save.await.unwrap();
9529 }
9530
9531 #[gpui::test]
9532 async fn test_completion(cx: &mut gpui::TestAppContext) {
9533 let mut cx = EditorLspTestContext::new_rust(
9534 lsp::ServerCapabilities {
9535 completion_provider: Some(lsp::CompletionOptions {
9536 trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
9537 ..Default::default()
9538 }),
9539 ..Default::default()
9540 },
9541 cx,
9542 )
9543 .await;
9544
9545 cx.set_state(indoc! {"
9546 one|
9547 two
9548 three"});
9549 cx.simulate_keystroke(".");
9550 handle_completion_request(
9551 &mut cx,
9552 indoc! {"
9553 one.|<>
9554 two
9555 three"},
9556 vec!["first_completion", "second_completion"],
9557 )
9558 .await;
9559 cx.condition(|editor, _| editor.context_menu_visible())
9560 .await;
9561 let apply_additional_edits = cx.update_editor(|editor, cx| {
9562 editor.move_down(&MoveDown, cx);
9563 editor
9564 .confirm_completion(&ConfirmCompletion::default(), cx)
9565 .unwrap()
9566 });
9567 cx.assert_editor_state(indoc! {"
9568 one.second_completion|
9569 two
9570 three"});
9571
9572 handle_resolve_completion_request(
9573 &mut cx,
9574 Some((
9575 indoc! {"
9576 one.second_completion
9577 two
9578 three<>"},
9579 "\nadditional edit",
9580 )),
9581 )
9582 .await;
9583 apply_additional_edits.await.unwrap();
9584 cx.assert_editor_state(indoc! {"
9585 one.second_completion|
9586 two
9587 three
9588 additional edit"});
9589
9590 cx.set_state(indoc! {"
9591 one.second_completion
9592 two|
9593 three|
9594 additional edit"});
9595 cx.simulate_keystroke(" ");
9596 assert!(cx.editor(|e, _| e.context_menu.is_none()));
9597 cx.simulate_keystroke("s");
9598 assert!(cx.editor(|e, _| e.context_menu.is_none()));
9599
9600 cx.assert_editor_state(indoc! {"
9601 one.second_completion
9602 two s|
9603 three s|
9604 additional edit"});
9605 handle_completion_request(
9606 &mut cx,
9607 indoc! {"
9608 one.second_completion
9609 two s
9610 three <s|>
9611 additional edit"},
9612 vec!["fourth_completion", "fifth_completion", "sixth_completion"],
9613 )
9614 .await;
9615 cx.condition(|editor, _| editor.context_menu_visible())
9616 .await;
9617
9618 cx.simulate_keystroke("i");
9619
9620 handle_completion_request(
9621 &mut cx,
9622 indoc! {"
9623 one.second_completion
9624 two si
9625 three <si|>
9626 additional edit"},
9627 vec!["fourth_completion", "fifth_completion", "sixth_completion"],
9628 )
9629 .await;
9630 cx.condition(|editor, _| editor.context_menu_visible())
9631 .await;
9632
9633 let apply_additional_edits = cx.update_editor(|editor, cx| {
9634 editor
9635 .confirm_completion(&ConfirmCompletion::default(), cx)
9636 .unwrap()
9637 });
9638 cx.assert_editor_state(indoc! {"
9639 one.second_completion
9640 two sixth_completion|
9641 three sixth_completion|
9642 additional edit"});
9643
9644 handle_resolve_completion_request(&mut cx, None).await;
9645 apply_additional_edits.await.unwrap();
9646
9647 cx.update(|cx| {
9648 cx.update_global::<Settings, _, _>(|settings, _| {
9649 settings.show_completions_on_input = false;
9650 })
9651 });
9652 cx.set_state("editor|");
9653 cx.simulate_keystroke(".");
9654 assert!(cx.editor(|e, _| e.context_menu.is_none()));
9655 cx.simulate_keystrokes(["c", "l", "o"]);
9656 cx.assert_editor_state("editor.clo|");
9657 assert!(cx.editor(|e, _| e.context_menu.is_none()));
9658 cx.update_editor(|editor, cx| {
9659 editor.show_completions(&ShowCompletions, cx);
9660 });
9661 handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
9662 cx.condition(|editor, _| editor.context_menu_visible())
9663 .await;
9664 let apply_additional_edits = cx.update_editor(|editor, cx| {
9665 editor
9666 .confirm_completion(&ConfirmCompletion::default(), cx)
9667 .unwrap()
9668 });
9669 cx.assert_editor_state("editor.close|");
9670 handle_resolve_completion_request(&mut cx, None).await;
9671 apply_additional_edits.await.unwrap();
9672
9673 // Handle completion request passing a marked string specifying where the completion
9674 // should be triggered from using '|' character, what range should be replaced, and what completions
9675 // should be returned using '<' and '>' to delimit the range
9676 async fn handle_completion_request<'a>(
9677 cx: &mut EditorLspTestContext<'a>,
9678 marked_string: &str,
9679 completions: Vec<&'static str>,
9680 ) {
9681 let complete_from_marker: TextRangeMarker = '|'.into();
9682 let replace_range_marker: TextRangeMarker = ('<', '>').into();
9683 let (_, mut marked_ranges) = marked_text_ranges_by(
9684 marked_string,
9685 vec![complete_from_marker.clone(), replace_range_marker.clone()],
9686 );
9687
9688 let complete_from_position =
9689 cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
9690 let replace_range =
9691 cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
9692
9693 cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
9694 let completions = completions.clone();
9695 async move {
9696 assert_eq!(params.text_document_position.text_document.uri, url.clone());
9697 assert_eq!(
9698 params.text_document_position.position,
9699 complete_from_position
9700 );
9701 Ok(Some(lsp::CompletionResponse::Array(
9702 completions
9703 .iter()
9704 .map(|completion_text| lsp::CompletionItem {
9705 label: completion_text.to_string(),
9706 text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
9707 range: replace_range.clone(),
9708 new_text: completion_text.to_string(),
9709 })),
9710 ..Default::default()
9711 })
9712 .collect(),
9713 )))
9714 }
9715 })
9716 .next()
9717 .await;
9718 }
9719
9720 async fn handle_resolve_completion_request<'a>(
9721 cx: &mut EditorLspTestContext<'a>,
9722 edit: Option<(&'static str, &'static str)>,
9723 ) {
9724 let edit = edit.map(|(marked_string, new_text)| {
9725 let replace_range_marker: TextRangeMarker = ('<', '>').into();
9726 let (_, mut marked_ranges) =
9727 marked_text_ranges_by(marked_string, vec![replace_range_marker.clone()]);
9728
9729 let replace_range = cx
9730 .to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
9731
9732 vec![lsp::TextEdit::new(replace_range, new_text.to_string())]
9733 });
9734
9735 cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
9736 let edit = edit.clone();
9737 async move {
9738 Ok(lsp::CompletionItem {
9739 additional_text_edits: edit,
9740 ..Default::default()
9741 })
9742 }
9743 })
9744 .next()
9745 .await;
9746 }
9747 }
9748
9749 #[gpui::test]
9750 async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
9751 cx.update(|cx| cx.set_global(Settings::test(cx)));
9752 let language = Arc::new(Language::new(
9753 LanguageConfig {
9754 line_comment: Some("// ".to_string()),
9755 ..Default::default()
9756 },
9757 Some(tree_sitter_rust::language()),
9758 ));
9759
9760 let text = "
9761 fn a() {
9762 //b();
9763 // c();
9764 // d();
9765 }
9766 "
9767 .unindent();
9768
9769 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9770 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9771 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9772
9773 view.update(cx, |editor, cx| {
9774 // If multiple selections intersect a line, the line is only
9775 // toggled once.
9776 editor.change_selections(None, cx, |s| {
9777 s.select_display_ranges([
9778 DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3),
9779 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6),
9780 ])
9781 });
9782 editor.toggle_comments(&ToggleComments, cx);
9783 assert_eq!(
9784 editor.text(cx),
9785 "
9786 fn a() {
9787 b();
9788 c();
9789 d();
9790 }
9791 "
9792 .unindent()
9793 );
9794
9795 // The comment prefix is inserted at the same column for every line
9796 // in a selection.
9797 editor.change_selections(None, cx, |s| {
9798 s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)])
9799 });
9800 editor.toggle_comments(&ToggleComments, cx);
9801 assert_eq!(
9802 editor.text(cx),
9803 "
9804 fn a() {
9805 // b();
9806 // c();
9807 // d();
9808 }
9809 "
9810 .unindent()
9811 );
9812
9813 // If a selection ends at the beginning of a line, that line is not toggled.
9814 editor.change_selections(None, cx, |s| {
9815 s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)])
9816 });
9817 editor.toggle_comments(&ToggleComments, cx);
9818 assert_eq!(
9819 editor.text(cx),
9820 "
9821 fn a() {
9822 // b();
9823 c();
9824 // d();
9825 }
9826 "
9827 .unindent()
9828 );
9829 });
9830 }
9831
9832 #[gpui::test]
9833 fn test_editing_disjoint_excerpts(cx: &mut gpui::MutableAppContext) {
9834 cx.set_global(Settings::test(cx));
9835 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
9836 let multibuffer = cx.add_model(|cx| {
9837 let mut multibuffer = MultiBuffer::new(0);
9838 multibuffer.push_excerpts(
9839 buffer.clone(),
9840 [
9841 ExcerptRange {
9842 context: Point::new(0, 0)..Point::new(0, 4),
9843 primary: None,
9844 },
9845 ExcerptRange {
9846 context: Point::new(1, 0)..Point::new(1, 4),
9847 primary: None,
9848 },
9849 ],
9850 cx,
9851 );
9852 multibuffer
9853 });
9854
9855 assert_eq!(multibuffer.read(cx).read(cx).text(), "aaaa\nbbbb");
9856
9857 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
9858 view.update(cx, |view, cx| {
9859 assert_eq!(view.text(cx), "aaaa\nbbbb");
9860 view.change_selections(None, cx, |s| {
9861 s.select_ranges([
9862 Point::new(0, 0)..Point::new(0, 0),
9863 Point::new(1, 0)..Point::new(1, 0),
9864 ])
9865 });
9866
9867 view.handle_input(&Input("X".to_string()), cx);
9868 assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
9869 assert_eq!(
9870 view.selections.ranges(cx),
9871 [
9872 Point::new(0, 1)..Point::new(0, 1),
9873 Point::new(1, 1)..Point::new(1, 1),
9874 ]
9875 )
9876 });
9877 }
9878
9879 #[gpui::test]
9880 fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) {
9881 cx.set_global(Settings::test(cx));
9882 let (initial_text, excerpt_ranges) = marked_text_ranges(indoc! {"
9883 [aaaa
9884 (bbbb]
9885 cccc)"});
9886 let excerpt_ranges = excerpt_ranges.into_iter().map(|context| ExcerptRange {
9887 context,
9888 primary: None,
9889 });
9890 let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx));
9891 let multibuffer = cx.add_model(|cx| {
9892 let mut multibuffer = MultiBuffer::new(0);
9893 multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
9894 multibuffer
9895 });
9896
9897 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
9898 view.update(cx, |view, cx| {
9899 let (expected_text, selection_ranges) = marked_text_ranges(indoc! {"
9900 aaaa
9901 b|bbb
9902 b|bb|b
9903 cccc"});
9904 assert_eq!(view.text(cx), expected_text);
9905 view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
9906
9907 view.handle_input(&Input("X".to_string()), cx);
9908
9909 let (expected_text, expected_selections) = marked_text_ranges(indoc! {"
9910 aaaa
9911 bX|bbXb
9912 bX|bbX|b
9913 cccc"});
9914 assert_eq!(view.text(cx), expected_text);
9915 assert_eq!(view.selections.ranges(cx), expected_selections);
9916
9917 view.newline(&Newline, cx);
9918 let (expected_text, expected_selections) = marked_text_ranges(indoc! {"
9919 aaaa
9920 bX
9921 |bbX
9922 b
9923 bX
9924 |bbX
9925 |b
9926 cccc"});
9927 assert_eq!(view.text(cx), expected_text);
9928 assert_eq!(view.selections.ranges(cx), expected_selections);
9929 });
9930 }
9931
9932 #[gpui::test]
9933 fn test_refresh_selections(cx: &mut gpui::MutableAppContext) {
9934 cx.set_global(Settings::test(cx));
9935 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
9936 let mut excerpt1_id = None;
9937 let multibuffer = cx.add_model(|cx| {
9938 let mut multibuffer = MultiBuffer::new(0);
9939 excerpt1_id = multibuffer
9940 .push_excerpts(
9941 buffer.clone(),
9942 [
9943 ExcerptRange {
9944 context: Point::new(0, 0)..Point::new(1, 4),
9945 primary: None,
9946 },
9947 ExcerptRange {
9948 context: Point::new(1, 0)..Point::new(2, 4),
9949 primary: None,
9950 },
9951 ],
9952 cx,
9953 )
9954 .into_iter()
9955 .next();
9956 multibuffer
9957 });
9958 assert_eq!(
9959 multibuffer.read(cx).read(cx).text(),
9960 "aaaa\nbbbb\nbbbb\ncccc"
9961 );
9962 let (_, editor) = cx.add_window(Default::default(), |cx| {
9963 let mut editor = build_editor(multibuffer.clone(), cx);
9964 let snapshot = editor.snapshot(cx);
9965 editor.change_selections(None, cx, |s| {
9966 s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
9967 });
9968 editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
9969 assert_eq!(
9970 editor.selections.ranges(cx),
9971 [
9972 Point::new(1, 3)..Point::new(1, 3),
9973 Point::new(2, 1)..Point::new(2, 1),
9974 ]
9975 );
9976 editor
9977 });
9978
9979 // Refreshing selections is a no-op when excerpts haven't changed.
9980 editor.update(cx, |editor, cx| {
9981 editor.change_selections(None, cx, |s| {
9982 s.refresh();
9983 });
9984 assert_eq!(
9985 editor.selections.ranges(cx),
9986 [
9987 Point::new(1, 3)..Point::new(1, 3),
9988 Point::new(2, 1)..Point::new(2, 1),
9989 ]
9990 );
9991 });
9992
9993 multibuffer.update(cx, |multibuffer, cx| {
9994 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
9995 });
9996 editor.update(cx, |editor, cx| {
9997 // Removing an excerpt causes the first selection to become degenerate.
9998 assert_eq!(
9999 editor.selections.ranges(cx),
10000 [
10001 Point::new(0, 0)..Point::new(0, 0),
10002 Point::new(0, 1)..Point::new(0, 1)
10003 ]
10004 );
10005
10006 // Refreshing selections will relocate the first selection to the original buffer
10007 // location.
10008 editor.change_selections(None, cx, |s| {
10009 s.refresh();
10010 });
10011 assert_eq!(
10012 editor.selections.ranges(cx),
10013 [
10014 Point::new(0, 1)..Point::new(0, 1),
10015 Point::new(0, 3)..Point::new(0, 3)
10016 ]
10017 );
10018 assert!(editor.selections.pending_anchor().is_some());
10019 });
10020 }
10021
10022 #[gpui::test]
10023 fn test_refresh_selections_while_selecting_with_mouse(cx: &mut gpui::MutableAppContext) {
10024 cx.set_global(Settings::test(cx));
10025 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
10026 let mut excerpt1_id = None;
10027 let multibuffer = cx.add_model(|cx| {
10028 let mut multibuffer = MultiBuffer::new(0);
10029 excerpt1_id = multibuffer
10030 .push_excerpts(
10031 buffer.clone(),
10032 [
10033 ExcerptRange {
10034 context: Point::new(0, 0)..Point::new(1, 4),
10035 primary: None,
10036 },
10037 ExcerptRange {
10038 context: Point::new(1, 0)..Point::new(2, 4),
10039 primary: None,
10040 },
10041 ],
10042 cx,
10043 )
10044 .into_iter()
10045 .next();
10046 multibuffer
10047 });
10048 assert_eq!(
10049 multibuffer.read(cx).read(cx).text(),
10050 "aaaa\nbbbb\nbbbb\ncccc"
10051 );
10052 let (_, editor) = cx.add_window(Default::default(), |cx| {
10053 let mut editor = build_editor(multibuffer.clone(), cx);
10054 let snapshot = editor.snapshot(cx);
10055 editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
10056 assert_eq!(
10057 editor.selections.ranges(cx),
10058 [Point::new(1, 3)..Point::new(1, 3)]
10059 );
10060 editor
10061 });
10062
10063 multibuffer.update(cx, |multibuffer, cx| {
10064 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
10065 });
10066 editor.update(cx, |editor, cx| {
10067 assert_eq!(
10068 editor.selections.ranges(cx),
10069 [Point::new(0, 0)..Point::new(0, 0)]
10070 );
10071
10072 // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
10073 editor.change_selections(None, cx, |s| {
10074 s.refresh();
10075 });
10076 assert_eq!(
10077 editor.selections.ranges(cx),
10078 [Point::new(0, 3)..Point::new(0, 3)]
10079 );
10080 assert!(editor.selections.pending_anchor().is_some());
10081 });
10082 }
10083
10084 #[gpui::test]
10085 async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
10086 cx.update(|cx| cx.set_global(Settings::test(cx)));
10087 let language = Arc::new(
10088 Language::new(
10089 LanguageConfig {
10090 brackets: vec![
10091 BracketPair {
10092 start: "{".to_string(),
10093 end: "}".to_string(),
10094 close: true,
10095 newline: true,
10096 },
10097 BracketPair {
10098 start: "/* ".to_string(),
10099 end: " */".to_string(),
10100 close: true,
10101 newline: true,
10102 },
10103 ],
10104 ..Default::default()
10105 },
10106 Some(tree_sitter_rust::language()),
10107 )
10108 .with_indents_query("")
10109 .unwrap(),
10110 );
10111
10112 let text = concat!(
10113 "{ }\n", // Suppress rustfmt
10114 " x\n", //
10115 " /* */\n", //
10116 "x\n", //
10117 "{{} }\n", //
10118 );
10119
10120 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
10121 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10122 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
10123 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
10124 .await;
10125
10126 view.update(cx, |view, cx| {
10127 view.change_selections(None, cx, |s| {
10128 s.select_display_ranges([
10129 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
10130 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
10131 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
10132 ])
10133 });
10134 view.newline(&Newline, cx);
10135
10136 assert_eq!(
10137 view.buffer().read(cx).read(cx).text(),
10138 concat!(
10139 "{ \n", // Suppress rustfmt
10140 "\n", //
10141 "}\n", //
10142 " x\n", //
10143 " /* \n", //
10144 " \n", //
10145 " */\n", //
10146 "x\n", //
10147 "{{} \n", //
10148 "}\n", //
10149 )
10150 );
10151 });
10152 }
10153
10154 #[gpui::test]
10155 fn test_highlighted_ranges(cx: &mut gpui::MutableAppContext) {
10156 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10157
10158 cx.set_global(Settings::test(cx));
10159 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
10160
10161 editor.update(cx, |editor, cx| {
10162 struct Type1;
10163 struct Type2;
10164
10165 let buffer = buffer.read(cx).snapshot(cx);
10166
10167 let anchor_range = |range: Range<Point>| {
10168 buffer.anchor_after(range.start)..buffer.anchor_after(range.end)
10169 };
10170
10171 editor.highlight_background::<Type1>(
10172 vec![
10173 anchor_range(Point::new(2, 1)..Point::new(2, 3)),
10174 anchor_range(Point::new(4, 2)..Point::new(4, 4)),
10175 anchor_range(Point::new(6, 3)..Point::new(6, 5)),
10176 anchor_range(Point::new(8, 4)..Point::new(8, 6)),
10177 ],
10178 |_| Color::red(),
10179 cx,
10180 );
10181 editor.highlight_background::<Type2>(
10182 vec![
10183 anchor_range(Point::new(3, 2)..Point::new(3, 5)),
10184 anchor_range(Point::new(5, 3)..Point::new(5, 6)),
10185 anchor_range(Point::new(7, 4)..Point::new(7, 7)),
10186 anchor_range(Point::new(9, 5)..Point::new(9, 8)),
10187 ],
10188 |_| Color::green(),
10189 cx,
10190 );
10191
10192 let snapshot = editor.snapshot(cx);
10193 let mut highlighted_ranges = editor.background_highlights_in_range(
10194 anchor_range(Point::new(3, 4)..Point::new(7, 4)),
10195 &snapshot,
10196 cx.global::<Settings>().theme.as_ref(),
10197 );
10198 // Enforce a consistent ordering based on color without relying on the ordering of the
10199 // highlight's `TypeId` which is non-deterministic.
10200 highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
10201 assert_eq!(
10202 highlighted_ranges,
10203 &[
10204 (
10205 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
10206 Color::green(),
10207 ),
10208 (
10209 DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
10210 Color::green(),
10211 ),
10212 (
10213 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
10214 Color::red(),
10215 ),
10216 (
10217 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
10218 Color::red(),
10219 ),
10220 ]
10221 );
10222 assert_eq!(
10223 editor.background_highlights_in_range(
10224 anchor_range(Point::new(5, 6)..Point::new(6, 4)),
10225 &snapshot,
10226 cx.global::<Settings>().theme.as_ref(),
10227 ),
10228 &[(
10229 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
10230 Color::red(),
10231 )]
10232 );
10233 });
10234 }
10235
10236 #[gpui::test]
10237 fn test_following(cx: &mut gpui::MutableAppContext) {
10238 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10239
10240 cx.set_global(Settings::test(cx));
10241
10242 let (_, leader) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
10243 let (_, follower) = cx.add_window(
10244 WindowOptions {
10245 bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
10246 ..Default::default()
10247 },
10248 |cx| build_editor(buffer.clone(), cx),
10249 );
10250
10251 let pending_update = Rc::new(RefCell::new(None));
10252 follower.update(cx, {
10253 let update = pending_update.clone();
10254 |_, cx| {
10255 cx.subscribe(&leader, move |_, leader, event, cx| {
10256 leader
10257 .read(cx)
10258 .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
10259 })
10260 .detach();
10261 }
10262 });
10263
10264 // Update the selections only
10265 leader.update(cx, |leader, cx| {
10266 leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
10267 });
10268 follower.update(cx, |follower, cx| {
10269 follower
10270 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10271 .unwrap();
10272 });
10273 assert_eq!(follower.read(cx).selections.ranges(cx), vec![1..1]);
10274
10275 // Update the scroll position only
10276 leader.update(cx, |leader, cx| {
10277 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
10278 });
10279 follower.update(cx, |follower, cx| {
10280 follower
10281 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10282 .unwrap();
10283 });
10284 assert_eq!(
10285 follower.update(cx, |follower, cx| follower.scroll_position(cx)),
10286 vec2f(1.5, 3.5)
10287 );
10288
10289 // Update the selections and scroll position
10290 leader.update(cx, |leader, cx| {
10291 leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
10292 leader.request_autoscroll(Autoscroll::Newest, cx);
10293 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
10294 });
10295 follower.update(cx, |follower, cx| {
10296 let initial_scroll_position = follower.scroll_position(cx);
10297 follower
10298 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10299 .unwrap();
10300 assert_eq!(follower.scroll_position(cx), initial_scroll_position);
10301 assert!(follower.autoscroll_request.is_some());
10302 });
10303 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0]);
10304
10305 // Creating a pending selection that precedes another selection
10306 leader.update(cx, |leader, cx| {
10307 leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
10308 leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
10309 });
10310 follower.update(cx, |follower, cx| {
10311 follower
10312 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10313 .unwrap();
10314 });
10315 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0, 1..1]);
10316
10317 // Extend the pending selection so that it surrounds another selection
10318 leader.update(cx, |leader, cx| {
10319 leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
10320 });
10321 follower.update(cx, |follower, cx| {
10322 follower
10323 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10324 .unwrap();
10325 });
10326 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..2]);
10327 }
10328
10329 #[test]
10330 fn test_combine_syntax_and_fuzzy_match_highlights() {
10331 let string = "abcdefghijklmnop";
10332 let syntax_ranges = [
10333 (
10334 0..3,
10335 HighlightStyle {
10336 color: Some(Color::red()),
10337 ..Default::default()
10338 },
10339 ),
10340 (
10341 4..8,
10342 HighlightStyle {
10343 color: Some(Color::green()),
10344 ..Default::default()
10345 },
10346 ),
10347 ];
10348 let match_indices = [4, 6, 7, 8];
10349 assert_eq!(
10350 combine_syntax_and_fuzzy_match_highlights(
10351 &string,
10352 Default::default(),
10353 syntax_ranges.into_iter(),
10354 &match_indices,
10355 ),
10356 &[
10357 (
10358 0..3,
10359 HighlightStyle {
10360 color: Some(Color::red()),
10361 ..Default::default()
10362 },
10363 ),
10364 (
10365 4..5,
10366 HighlightStyle {
10367 color: Some(Color::green()),
10368 weight: Some(fonts::Weight::BOLD),
10369 ..Default::default()
10370 },
10371 ),
10372 (
10373 5..6,
10374 HighlightStyle {
10375 color: Some(Color::green()),
10376 ..Default::default()
10377 },
10378 ),
10379 (
10380 6..8,
10381 HighlightStyle {
10382 color: Some(Color::green()),
10383 weight: Some(fonts::Weight::BOLD),
10384 ..Default::default()
10385 },
10386 ),
10387 (
10388 8..9,
10389 HighlightStyle {
10390 weight: Some(fonts::Weight::BOLD),
10391 ..Default::default()
10392 },
10393 ),
10394 ]
10395 );
10396 }
10397
10398 fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
10399 let point = DisplayPoint::new(row as u32, column as u32);
10400 point..point
10401 }
10402
10403 fn assert_selection_ranges(
10404 marked_text: &str,
10405 selection_marker_pairs: Vec<(char, char)>,
10406 view: &mut Editor,
10407 cx: &mut ViewContext<Editor>,
10408 ) {
10409 let snapshot = view.snapshot(cx).display_snapshot;
10410 let mut marker_chars = Vec::new();
10411 for (start, end) in selection_marker_pairs.iter() {
10412 marker_chars.push(*start);
10413 marker_chars.push(*end);
10414 }
10415 let (_, markers) = marked_text_by(marked_text, marker_chars);
10416 let asserted_ranges: Vec<Range<DisplayPoint>> = selection_marker_pairs
10417 .iter()
10418 .map(|(start, end)| {
10419 let start = markers.get(start).unwrap()[0].to_display_point(&snapshot);
10420 let end = markers.get(end).unwrap()[0].to_display_point(&snapshot);
10421 start..end
10422 })
10423 .collect();
10424 assert_eq!(
10425 view.selections.display_ranges(cx),
10426 &asserted_ranges[..],
10427 "Assert selections are {}",
10428 marked_text
10429 );
10430 }
10431}
10432
10433trait RangeExt<T> {
10434 fn sorted(&self) -> Range<T>;
10435 fn to_inclusive(&self) -> RangeInclusive<T>;
10436}
10437
10438impl<T: Ord + Clone> RangeExt<T> for Range<T> {
10439 fn sorted(&self) -> Self {
10440 cmp::min(&self.start, &self.end).clone()..cmp::max(&self.start, &self.end).clone()
10441 }
10442
10443 fn to_inclusive(&self) -> RangeInclusive<T> {
10444 self.start.clone()..=self.end.clone()
10445 }
10446}
10447
10448trait RangeToAnchorExt {
10449 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
10450}
10451
10452impl<T: ToOffset> RangeToAnchorExt for Range<T> {
10453 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
10454 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
10455 }
10456}