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