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::Dirtied => cx.emit(Event::Dirtied),
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 Dirtied,
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!(event, Event::Edited | Event::BufferEdited | Event::Dirtied) {
6185 events.borrow_mut().push(("editor1", *event));
6186 }
6187 })
6188 .detach();
6189 Editor::for_buffer(buffer.clone(), None, cx)
6190 }
6191 });
6192 let (_, editor2) = cx.add_window(Default::default(), {
6193 let events = events.clone();
6194 |cx| {
6195 cx.subscribe(&cx.handle(), move |_, _, event, _| {
6196 if matches!(event, Event::Edited | Event::BufferEdited | Event::Dirtied) {
6197 events.borrow_mut().push(("editor2", *event));
6198 }
6199 })
6200 .detach();
6201 Editor::for_buffer(buffer.clone(), None, cx)
6202 }
6203 });
6204 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6205
6206 // Mutating editor 1 will emit an `Edited` event only for that editor.
6207 editor1.update(cx, |editor, cx| editor.insert("X", cx));
6208 assert_eq!(
6209 mem::take(&mut *events.borrow_mut()),
6210 [
6211 ("editor1", Event::Edited),
6212 ("editor1", Event::BufferEdited),
6213 ("editor2", Event::BufferEdited),
6214 ("editor1", Event::Dirtied),
6215 ("editor2", Event::Dirtied)
6216 ]
6217 );
6218
6219 // Mutating editor 2 will emit an `Edited` event only for that editor.
6220 editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
6221 assert_eq!(
6222 mem::take(&mut *events.borrow_mut()),
6223 [
6224 ("editor2", Event::Edited),
6225 ("editor1", Event::BufferEdited),
6226 ("editor2", Event::BufferEdited),
6227 ]
6228 );
6229
6230 // Undoing on editor 1 will emit an `Edited` event only for that editor.
6231 editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
6232 assert_eq!(
6233 mem::take(&mut *events.borrow_mut()),
6234 [
6235 ("editor1", Event::Edited),
6236 ("editor1", Event::BufferEdited),
6237 ("editor2", Event::BufferEdited),
6238 ]
6239 );
6240
6241 // Redoing on editor 1 will emit an `Edited` event only for that editor.
6242 editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
6243 assert_eq!(
6244 mem::take(&mut *events.borrow_mut()),
6245 [
6246 ("editor1", Event::Edited),
6247 ("editor1", Event::BufferEdited),
6248 ("editor2", Event::BufferEdited),
6249 ]
6250 );
6251
6252 // Undoing on editor 2 will emit an `Edited` event only for that editor.
6253 editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
6254 assert_eq!(
6255 mem::take(&mut *events.borrow_mut()),
6256 [
6257 ("editor2", Event::Edited),
6258 ("editor1", Event::BufferEdited),
6259 ("editor2", Event::BufferEdited),
6260 ]
6261 );
6262
6263 // Redoing on editor 2 will emit an `Edited` event only for that editor.
6264 editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
6265 assert_eq!(
6266 mem::take(&mut *events.borrow_mut()),
6267 [
6268 ("editor2", Event::Edited),
6269 ("editor1", Event::BufferEdited),
6270 ("editor2", Event::BufferEdited),
6271 ]
6272 );
6273
6274 // No event is emitted when the mutation is a no-op.
6275 editor2.update(cx, |editor, cx| {
6276 editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
6277
6278 editor.backspace(&Backspace, cx);
6279 });
6280 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6281 }
6282
6283 #[gpui::test]
6284 fn test_undo_redo_with_selection_restoration(cx: &mut MutableAppContext) {
6285 cx.set_global(Settings::test(cx));
6286 let mut now = Instant::now();
6287 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6288 let group_interval = buffer.read(cx).transaction_group_interval();
6289 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
6290 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6291
6292 editor.update(cx, |editor, cx| {
6293 editor.start_transaction_at(now, cx);
6294 editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
6295
6296 editor.insert("cd", cx);
6297 editor.end_transaction_at(now, cx);
6298 assert_eq!(editor.text(cx), "12cd56");
6299 assert_eq!(editor.selections.ranges(cx), vec![4..4]);
6300
6301 editor.start_transaction_at(now, cx);
6302 editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
6303 editor.insert("e", cx);
6304 editor.end_transaction_at(now, cx);
6305 assert_eq!(editor.text(cx), "12cde6");
6306 assert_eq!(editor.selections.ranges(cx), vec![5..5]);
6307
6308 now += group_interval + Duration::from_millis(1);
6309 editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
6310
6311 // Simulate an edit in another editor
6312 buffer.update(cx, |buffer, cx| {
6313 buffer.start_transaction_at(now, cx);
6314 buffer.edit([(0..1, "a")], cx);
6315 buffer.edit([(1..1, "b")], cx);
6316 buffer.end_transaction_at(now, cx);
6317 });
6318
6319 assert_eq!(editor.text(cx), "ab2cde6");
6320 assert_eq!(editor.selections.ranges(cx), vec![3..3]);
6321
6322 // Last transaction happened past the group interval in a different editor.
6323 // Undo it individually and don't restore selections.
6324 editor.undo(&Undo, cx);
6325 assert_eq!(editor.text(cx), "12cde6");
6326 assert_eq!(editor.selections.ranges(cx), vec![2..2]);
6327
6328 // First two transactions happened within the group interval in this editor.
6329 // Undo them together and restore selections.
6330 editor.undo(&Undo, cx);
6331 editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
6332 assert_eq!(editor.text(cx), "123456");
6333 assert_eq!(editor.selections.ranges(cx), vec![0..0]);
6334
6335 // Redo the first two transactions together.
6336 editor.redo(&Redo, cx);
6337 assert_eq!(editor.text(cx), "12cde6");
6338 assert_eq!(editor.selections.ranges(cx), vec![5..5]);
6339
6340 // Redo the last transaction on its own.
6341 editor.redo(&Redo, cx);
6342 assert_eq!(editor.text(cx), "ab2cde6");
6343 assert_eq!(editor.selections.ranges(cx), vec![6..6]);
6344
6345 // Test empty transactions.
6346 editor.start_transaction_at(now, cx);
6347 editor.end_transaction_at(now, cx);
6348 editor.undo(&Undo, cx);
6349 assert_eq!(editor.text(cx), "12cde6");
6350 });
6351 }
6352
6353 #[gpui::test]
6354 fn test_selection_with_mouse(cx: &mut gpui::MutableAppContext) {
6355 cx.set_global(Settings::test(cx));
6356
6357 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
6358 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6359 editor.update(cx, |view, cx| {
6360 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
6361 });
6362 assert_eq!(
6363 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6364 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
6365 );
6366
6367 editor.update(cx, |view, cx| {
6368 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6369 });
6370
6371 assert_eq!(
6372 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6373 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6374 );
6375
6376 editor.update(cx, |view, cx| {
6377 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6378 });
6379
6380 assert_eq!(
6381 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6382 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
6383 );
6384
6385 editor.update(cx, |view, cx| {
6386 view.end_selection(cx);
6387 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6388 });
6389
6390 assert_eq!(
6391 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6392 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
6393 );
6394
6395 editor.update(cx, |view, cx| {
6396 view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx);
6397 view.update_selection(DisplayPoint::new(0, 0), 0, Vector2F::zero(), cx);
6398 });
6399
6400 assert_eq!(
6401 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6402 [
6403 DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
6404 DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
6405 ]
6406 );
6407
6408 editor.update(cx, |view, cx| {
6409 view.end_selection(cx);
6410 });
6411
6412 assert_eq!(
6413 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6414 [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
6415 );
6416 }
6417
6418 #[gpui::test]
6419 fn test_canceling_pending_selection(cx: &mut gpui::MutableAppContext) {
6420 cx.set_global(Settings::test(cx));
6421 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
6422 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6423
6424 view.update(cx, |view, cx| {
6425 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
6426 assert_eq!(
6427 view.selections.display_ranges(cx),
6428 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
6429 );
6430 });
6431
6432 view.update(cx, |view, cx| {
6433 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6434 assert_eq!(
6435 view.selections.display_ranges(cx),
6436 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6437 );
6438 });
6439
6440 view.update(cx, |view, cx| {
6441 view.cancel(&Cancel, cx);
6442 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6443 assert_eq!(
6444 view.selections.display_ranges(cx),
6445 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6446 );
6447 });
6448 }
6449
6450 #[gpui::test]
6451 fn test_clone_with_selections(cx: &mut gpui::MutableAppContext) {
6452 let (text, selection_ranges) = marked_text_ranges(indoc! {"
6453 The qu[ick brown
6454 fox jum]ps over
6455 the lazy dog
6456 "});
6457 cx.set_global(Settings::test(cx));
6458 let buffer = MultiBuffer::build_simple(&text, cx);
6459
6460 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6461
6462 let cloned_editor = view.update(cx, |view, cx| {
6463 view.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
6464 view.clone(cx)
6465 });
6466
6467 assert_set_eq!(cloned_editor.selections.ranges(cx), selection_ranges);
6468 }
6469
6470 #[gpui::test]
6471 fn test_navigation_history(cx: &mut gpui::MutableAppContext) {
6472 cx.set_global(Settings::test(cx));
6473 use workspace::Item;
6474 let nav_history = Rc::new(RefCell::new(workspace::NavHistory::default()));
6475 let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
6476
6477 cx.add_window(Default::default(), |cx| {
6478 let mut editor = build_editor(buffer.clone(), cx);
6479 editor.nav_history = Some(ItemNavHistory::new(nav_history.clone(), &cx.handle()));
6480
6481 // Move the cursor a small distance.
6482 // Nothing is added to the navigation history.
6483 editor.change_selections(None, cx, |s| {
6484 s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)])
6485 });
6486 editor.change_selections(None, cx, |s| {
6487 s.select_display_ranges([DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)])
6488 });
6489 assert!(nav_history.borrow_mut().pop_backward().is_none());
6490
6491 // Move the cursor a large distance.
6492 // The history can jump back to the previous position.
6493 editor.change_selections(None, cx, |s| {
6494 s.select_display_ranges([DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)])
6495 });
6496 let nav_entry = nav_history.borrow_mut().pop_backward().unwrap();
6497 editor.navigate(nav_entry.data.unwrap(), cx);
6498 assert_eq!(nav_entry.item.id(), cx.view_id());
6499 assert_eq!(
6500 editor.selections.display_ranges(cx),
6501 &[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 small distance via the mouse.
6506 // Nothing is added to the navigation history.
6507 editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx);
6508 editor.end_selection(cx);
6509 assert_eq!(
6510 editor.selections.display_ranges(cx),
6511 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
6512 );
6513 assert!(nav_history.borrow_mut().pop_backward().is_none());
6514
6515 // Move the cursor a large distance via the mouse.
6516 // The history can jump back to the previous position.
6517 editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx);
6518 editor.end_selection(cx);
6519 assert_eq!(
6520 editor.selections.display_ranges(cx),
6521 &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]
6522 );
6523 let nav_entry = nav_history.borrow_mut().pop_backward().unwrap();
6524 editor.navigate(nav_entry.data.unwrap(), cx);
6525 assert_eq!(nav_entry.item.id(), cx.view_id());
6526 assert_eq!(
6527 editor.selections.display_ranges(cx),
6528 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
6529 );
6530 assert!(nav_history.borrow_mut().pop_backward().is_none());
6531
6532 // Set scroll position to check later
6533 editor.set_scroll_position(Vector2F::new(5.5, 5.5), cx);
6534 let original_scroll_position = editor.scroll_position;
6535 let original_scroll_top_anchor = editor.scroll_top_anchor.clone();
6536
6537 // Jump to the end of the document and adjust scroll
6538 editor.move_to_end(&MoveToEnd, cx);
6539 editor.set_scroll_position(Vector2F::new(-2.5, -0.5), cx);
6540 assert_ne!(editor.scroll_position, original_scroll_position);
6541 assert_ne!(editor.scroll_top_anchor, original_scroll_top_anchor);
6542
6543 let nav_entry = nav_history.borrow_mut().pop_backward().unwrap();
6544 editor.navigate(nav_entry.data.unwrap(), cx);
6545 assert_eq!(editor.scroll_position, original_scroll_position);
6546 assert_eq!(editor.scroll_top_anchor, original_scroll_top_anchor);
6547
6548 // Ensure we don't panic when navigation data contains invalid anchors *and* points.
6549 let mut invalid_anchor = editor.scroll_top_anchor.clone();
6550 invalid_anchor.text_anchor.buffer_id = Some(999);
6551 let invalid_point = Point::new(9999, 0);
6552 editor.navigate(
6553 Box::new(NavigationData {
6554 cursor_anchor: invalid_anchor.clone(),
6555 cursor_position: invalid_point,
6556 scroll_top_anchor: invalid_anchor.clone(),
6557 scroll_top_row: invalid_point.row,
6558 scroll_position: Default::default(),
6559 }),
6560 cx,
6561 );
6562 assert_eq!(
6563 editor.selections.display_ranges(cx),
6564 &[editor.max_point(cx)..editor.max_point(cx)]
6565 );
6566 assert_eq!(
6567 editor.scroll_position(cx),
6568 vec2f(0., editor.max_point(cx).row() as f32)
6569 );
6570
6571 editor
6572 });
6573 }
6574
6575 #[gpui::test]
6576 fn test_cancel(cx: &mut gpui::MutableAppContext) {
6577 cx.set_global(Settings::test(cx));
6578 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
6579 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6580
6581 view.update(cx, |view, cx| {
6582 view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
6583 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6584 view.end_selection(cx);
6585
6586 view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx);
6587 view.update_selection(DisplayPoint::new(0, 3), 0, Vector2F::zero(), cx);
6588 view.end_selection(cx);
6589 assert_eq!(
6590 view.selections.display_ranges(cx),
6591 [
6592 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
6593 DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1),
6594 ]
6595 );
6596 });
6597
6598 view.update(cx, |view, cx| {
6599 view.cancel(&Cancel, cx);
6600 assert_eq!(
6601 view.selections.display_ranges(cx),
6602 [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)]
6603 );
6604 });
6605
6606 view.update(cx, |view, cx| {
6607 view.cancel(&Cancel, cx);
6608 assert_eq!(
6609 view.selections.display_ranges(cx),
6610 [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)]
6611 );
6612 });
6613 }
6614
6615 #[gpui::test]
6616 fn test_fold(cx: &mut gpui::MutableAppContext) {
6617 cx.set_global(Settings::test(cx));
6618 let buffer = MultiBuffer::build_simple(
6619 &"
6620 impl Foo {
6621 // Hello!
6622
6623 fn a() {
6624 1
6625 }
6626
6627 fn b() {
6628 2
6629 }
6630
6631 fn c() {
6632 3
6633 }
6634 }
6635 "
6636 .unindent(),
6637 cx,
6638 );
6639 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6640
6641 view.update(cx, |view, cx| {
6642 view.change_selections(None, cx, |s| {
6643 s.select_display_ranges([DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)]);
6644 });
6645 view.fold(&Fold, cx);
6646 assert_eq!(
6647 view.display_text(cx),
6648 "
6649 impl Foo {
6650 // Hello!
6651
6652 fn a() {
6653 1
6654 }
6655
6656 fn b() {…
6657 }
6658
6659 fn c() {…
6660 }
6661 }
6662 "
6663 .unindent(),
6664 );
6665
6666 view.fold(&Fold, cx);
6667 assert_eq!(
6668 view.display_text(cx),
6669 "
6670 impl Foo {…
6671 }
6672 "
6673 .unindent(),
6674 );
6675
6676 view.unfold_lines(&UnfoldLines, cx);
6677 assert_eq!(
6678 view.display_text(cx),
6679 "
6680 impl Foo {
6681 // Hello!
6682
6683 fn a() {
6684 1
6685 }
6686
6687 fn b() {…
6688 }
6689
6690 fn c() {…
6691 }
6692 }
6693 "
6694 .unindent(),
6695 );
6696
6697 view.unfold_lines(&UnfoldLines, cx);
6698 assert_eq!(view.display_text(cx), buffer.read(cx).read(cx).text());
6699 });
6700 }
6701
6702 #[gpui::test]
6703 fn test_move_cursor(cx: &mut gpui::MutableAppContext) {
6704 cx.set_global(Settings::test(cx));
6705 let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
6706 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6707
6708 buffer.update(cx, |buffer, cx| {
6709 buffer.edit(
6710 vec![
6711 (Point::new(1, 0)..Point::new(1, 0), "\t"),
6712 (Point::new(1, 1)..Point::new(1, 1), "\t"),
6713 ],
6714 cx,
6715 );
6716 });
6717
6718 view.update(cx, |view, cx| {
6719 assert_eq!(
6720 view.selections.display_ranges(cx),
6721 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
6722 );
6723
6724 view.move_down(&MoveDown, cx);
6725 assert_eq!(
6726 view.selections.display_ranges(cx),
6727 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
6728 );
6729
6730 view.move_right(&MoveRight, cx);
6731 assert_eq!(
6732 view.selections.display_ranges(cx),
6733 &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
6734 );
6735
6736 view.move_left(&MoveLeft, cx);
6737 assert_eq!(
6738 view.selections.display_ranges(cx),
6739 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
6740 );
6741
6742 view.move_up(&MoveUp, cx);
6743 assert_eq!(
6744 view.selections.display_ranges(cx),
6745 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
6746 );
6747
6748 view.move_to_end(&MoveToEnd, cx);
6749 assert_eq!(
6750 view.selections.display_ranges(cx),
6751 &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)]
6752 );
6753
6754 view.move_to_beginning(&MoveToBeginning, cx);
6755 assert_eq!(
6756 view.selections.display_ranges(cx),
6757 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
6758 );
6759
6760 view.change_selections(None, cx, |s| {
6761 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]);
6762 });
6763 view.select_to_beginning(&SelectToBeginning, cx);
6764 assert_eq!(
6765 view.selections.display_ranges(cx),
6766 &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)]
6767 );
6768
6769 view.select_to_end(&SelectToEnd, cx);
6770 assert_eq!(
6771 view.selections.display_ranges(cx),
6772 &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)]
6773 );
6774 });
6775 }
6776
6777 #[gpui::test]
6778 fn test_move_cursor_multibyte(cx: &mut gpui::MutableAppContext) {
6779 cx.set_global(Settings::test(cx));
6780 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx);
6781 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6782
6783 assert_eq!('ⓐ'.len_utf8(), 3);
6784 assert_eq!('α'.len_utf8(), 2);
6785
6786 view.update(cx, |view, cx| {
6787 view.fold_ranges(
6788 vec![
6789 Point::new(0, 6)..Point::new(0, 12),
6790 Point::new(1, 2)..Point::new(1, 4),
6791 Point::new(2, 4)..Point::new(2, 8),
6792 ],
6793 cx,
6794 );
6795 assert_eq!(view.display_text(cx), "ⓐⓑ…ⓔ\nab…e\nαβ…ε\n");
6796
6797 view.move_right(&MoveRight, cx);
6798 assert_eq!(
6799 view.selections.display_ranges(cx),
6800 &[empty_range(0, "ⓐ".len())]
6801 );
6802 view.move_right(&MoveRight, cx);
6803 assert_eq!(
6804 view.selections.display_ranges(cx),
6805 &[empty_range(0, "ⓐⓑ".len())]
6806 );
6807 view.move_right(&MoveRight, cx);
6808 assert_eq!(
6809 view.selections.display_ranges(cx),
6810 &[empty_range(0, "ⓐⓑ…".len())]
6811 );
6812
6813 view.move_down(&MoveDown, cx);
6814 assert_eq!(
6815 view.selections.display_ranges(cx),
6816 &[empty_range(1, "ab…".len())]
6817 );
6818 view.move_left(&MoveLeft, cx);
6819 assert_eq!(
6820 view.selections.display_ranges(cx),
6821 &[empty_range(1, "ab".len())]
6822 );
6823 view.move_left(&MoveLeft, cx);
6824 assert_eq!(
6825 view.selections.display_ranges(cx),
6826 &[empty_range(1, "a".len())]
6827 );
6828
6829 view.move_down(&MoveDown, cx);
6830 assert_eq!(
6831 view.selections.display_ranges(cx),
6832 &[empty_range(2, "α".len())]
6833 );
6834 view.move_right(&MoveRight, cx);
6835 assert_eq!(
6836 view.selections.display_ranges(cx),
6837 &[empty_range(2, "αβ".len())]
6838 );
6839 view.move_right(&MoveRight, cx);
6840 assert_eq!(
6841 view.selections.display_ranges(cx),
6842 &[empty_range(2, "αβ…".len())]
6843 );
6844 view.move_right(&MoveRight, cx);
6845 assert_eq!(
6846 view.selections.display_ranges(cx),
6847 &[empty_range(2, "αβ…ε".len())]
6848 );
6849
6850 view.move_up(&MoveUp, cx);
6851 assert_eq!(
6852 view.selections.display_ranges(cx),
6853 &[empty_range(1, "ab…e".len())]
6854 );
6855 view.move_up(&MoveUp, cx);
6856 assert_eq!(
6857 view.selections.display_ranges(cx),
6858 &[empty_range(0, "ⓐⓑ…ⓔ".len())]
6859 );
6860 view.move_left(&MoveLeft, cx);
6861 assert_eq!(
6862 view.selections.display_ranges(cx),
6863 &[empty_range(0, "ⓐⓑ…".len())]
6864 );
6865 view.move_left(&MoveLeft, cx);
6866 assert_eq!(
6867 view.selections.display_ranges(cx),
6868 &[empty_range(0, "ⓐⓑ".len())]
6869 );
6870 view.move_left(&MoveLeft, cx);
6871 assert_eq!(
6872 view.selections.display_ranges(cx),
6873 &[empty_range(0, "ⓐ".len())]
6874 );
6875 });
6876 }
6877
6878 #[gpui::test]
6879 fn test_move_cursor_different_line_lengths(cx: &mut gpui::MutableAppContext) {
6880 cx.set_global(Settings::test(cx));
6881 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
6882 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6883 view.update(cx, |view, cx| {
6884 view.change_selections(None, cx, |s| {
6885 s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
6886 });
6887 view.move_down(&MoveDown, cx);
6888 assert_eq!(
6889 view.selections.display_ranges(cx),
6890 &[empty_range(1, "abcd".len())]
6891 );
6892
6893 view.move_down(&MoveDown, cx);
6894 assert_eq!(
6895 view.selections.display_ranges(cx),
6896 &[empty_range(2, "αβγ".len())]
6897 );
6898
6899 view.move_down(&MoveDown, cx);
6900 assert_eq!(
6901 view.selections.display_ranges(cx),
6902 &[empty_range(3, "abcd".len())]
6903 );
6904
6905 view.move_down(&MoveDown, cx);
6906 assert_eq!(
6907 view.selections.display_ranges(cx),
6908 &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
6909 );
6910
6911 view.move_up(&MoveUp, cx);
6912 assert_eq!(
6913 view.selections.display_ranges(cx),
6914 &[empty_range(3, "abcd".len())]
6915 );
6916
6917 view.move_up(&MoveUp, cx);
6918 assert_eq!(
6919 view.selections.display_ranges(cx),
6920 &[empty_range(2, "αβγ".len())]
6921 );
6922 });
6923 }
6924
6925 #[gpui::test]
6926 fn test_beginning_end_of_line(cx: &mut gpui::MutableAppContext) {
6927 cx.set_global(Settings::test(cx));
6928 let buffer = MultiBuffer::build_simple("abc\n def", cx);
6929 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6930 view.update(cx, |view, cx| {
6931 view.change_selections(None, cx, |s| {
6932 s.select_display_ranges([
6933 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
6934 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
6935 ]);
6936 });
6937 });
6938
6939 view.update(cx, |view, cx| {
6940 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
6941 assert_eq!(
6942 view.selections.display_ranges(cx),
6943 &[
6944 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
6945 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
6946 ]
6947 );
6948 });
6949
6950 view.update(cx, |view, cx| {
6951 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
6952 assert_eq!(
6953 view.selections.display_ranges(cx),
6954 &[
6955 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
6956 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
6957 ]
6958 );
6959 });
6960
6961 view.update(cx, |view, cx| {
6962 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
6963 assert_eq!(
6964 view.selections.display_ranges(cx),
6965 &[
6966 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
6967 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
6968 ]
6969 );
6970 });
6971
6972 view.update(cx, |view, cx| {
6973 view.move_to_end_of_line(&MoveToEndOfLine, cx);
6974 assert_eq!(
6975 view.selections.display_ranges(cx),
6976 &[
6977 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
6978 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
6979 ]
6980 );
6981 });
6982
6983 // Moving to the end of line again is a no-op.
6984 view.update(cx, |view, cx| {
6985 view.move_to_end_of_line(&MoveToEndOfLine, cx);
6986 assert_eq!(
6987 view.selections.display_ranges(cx),
6988 &[
6989 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
6990 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
6991 ]
6992 );
6993 });
6994
6995 view.update(cx, |view, cx| {
6996 view.move_left(&MoveLeft, cx);
6997 view.select_to_beginning_of_line(
6998 &SelectToBeginningOfLine {
6999 stop_at_soft_wraps: true,
7000 },
7001 cx,
7002 );
7003 assert_eq!(
7004 view.selections.display_ranges(cx),
7005 &[
7006 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7007 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7008 ]
7009 );
7010 });
7011
7012 view.update(cx, |view, cx| {
7013 view.select_to_beginning_of_line(
7014 &SelectToBeginningOfLine {
7015 stop_at_soft_wraps: true,
7016 },
7017 cx,
7018 );
7019 assert_eq!(
7020 view.selections.display_ranges(cx),
7021 &[
7022 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7023 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0),
7024 ]
7025 );
7026 });
7027
7028 view.update(cx, |view, cx| {
7029 view.select_to_beginning_of_line(
7030 &SelectToBeginningOfLine {
7031 stop_at_soft_wraps: true,
7032 },
7033 cx,
7034 );
7035 assert_eq!(
7036 view.selections.display_ranges(cx),
7037 &[
7038 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7039 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7040 ]
7041 );
7042 });
7043
7044 view.update(cx, |view, cx| {
7045 view.select_to_end_of_line(
7046 &SelectToEndOfLine {
7047 stop_at_soft_wraps: true,
7048 },
7049 cx,
7050 );
7051 assert_eq!(
7052 view.selections.display_ranges(cx),
7053 &[
7054 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
7055 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5),
7056 ]
7057 );
7058 });
7059
7060 view.update(cx, |view, cx| {
7061 view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
7062 assert_eq!(view.display_text(cx), "ab\n de");
7063 assert_eq!(
7064 view.selections.display_ranges(cx),
7065 &[
7066 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7067 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
7068 ]
7069 );
7070 });
7071
7072 view.update(cx, |view, cx| {
7073 view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
7074 assert_eq!(view.display_text(cx), "\n");
7075 assert_eq!(
7076 view.selections.display_ranges(cx),
7077 &[
7078 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7079 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7080 ]
7081 );
7082 });
7083 }
7084
7085 #[gpui::test]
7086 fn test_prev_next_word_boundary(cx: &mut gpui::MutableAppContext) {
7087 cx.set_global(Settings::test(cx));
7088 let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx);
7089 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7090 view.update(cx, |view, cx| {
7091 view.change_selections(None, cx, |s| {
7092 s.select_display_ranges([
7093 DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
7094 DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
7095 ])
7096 });
7097
7098 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7099 assert_selection_ranges(
7100 "use std::<>str::{foo, bar}\n\n {[]baz.qux()}",
7101 vec![('<', '>'), ('[', ']')],
7102 view,
7103 cx,
7104 );
7105
7106 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7107 assert_selection_ranges(
7108 "use std<>::str::{foo, bar}\n\n []{baz.qux()}",
7109 vec![('<', '>'), ('[', ']')],
7110 view,
7111 cx,
7112 );
7113
7114 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7115 assert_selection_ranges(
7116 "use <>std::str::{foo, bar}\n\n[] {baz.qux()}",
7117 vec![('<', '>'), ('[', ']')],
7118 view,
7119 cx,
7120 );
7121
7122 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7123 assert_selection_ranges(
7124 "<>use std::str::{foo, bar}\n[]\n {baz.qux()}",
7125 vec![('<', '>'), ('[', ']')],
7126 view,
7127 cx,
7128 );
7129
7130 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7131 assert_selection_ranges(
7132 "<>use std::str::{foo, bar[]}\n\n {baz.qux()}",
7133 vec![('<', '>'), ('[', ']')],
7134 view,
7135 cx,
7136 );
7137
7138 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7139 assert_selection_ranges(
7140 "use<> std::str::{foo, bar}[]\n\n {baz.qux()}",
7141 vec![('<', '>'), ('[', ']')],
7142 view,
7143 cx,
7144 );
7145
7146 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7147 assert_selection_ranges(
7148 "use std<>::str::{foo, bar}\n[]\n {baz.qux()}",
7149 vec![('<', '>'), ('[', ']')],
7150 view,
7151 cx,
7152 );
7153
7154 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7155 assert_selection_ranges(
7156 "use std::<>str::{foo, bar}\n\n {[]baz.qux()}",
7157 vec![('<', '>'), ('[', ']')],
7158 view,
7159 cx,
7160 );
7161
7162 view.move_right(&MoveRight, cx);
7163 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7164 assert_selection_ranges(
7165 "use std::>s<tr::{foo, bar}\n\n {]b[az.qux()}",
7166 vec![('<', '>'), ('[', ']')],
7167 view,
7168 cx,
7169 );
7170
7171 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7172 assert_selection_ranges(
7173 "use std>::s<tr::{foo, bar}\n\n ]{b[az.qux()}",
7174 vec![('<', '>'), ('[', ']')],
7175 view,
7176 cx,
7177 );
7178
7179 view.select_to_next_word_end(&SelectToNextWordEnd, cx);
7180 assert_selection_ranges(
7181 "use std::>s<tr::{foo, bar}\n\n {]b[az.qux()}",
7182 vec![('<', '>'), ('[', ']')],
7183 view,
7184 cx,
7185 );
7186 });
7187 }
7188
7189 #[gpui::test]
7190 fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut gpui::MutableAppContext) {
7191 cx.set_global(Settings::test(cx));
7192 let buffer = MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx);
7193 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7194
7195 view.update(cx, |view, cx| {
7196 view.set_wrap_width(Some(140.), cx);
7197 assert_eq!(
7198 view.display_text(cx),
7199 "use one::{\n two::three::\n four::five\n};"
7200 );
7201
7202 view.change_selections(None, cx, |s| {
7203 s.select_display_ranges([DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)]);
7204 });
7205
7206 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7207 assert_eq!(
7208 view.selections.display_ranges(cx),
7209 &[DisplayPoint::new(1, 9)..DisplayPoint::new(1, 9)]
7210 );
7211
7212 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7213 assert_eq!(
7214 view.selections.display_ranges(cx),
7215 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
7216 );
7217
7218 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7219 assert_eq!(
7220 view.selections.display_ranges(cx),
7221 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7222 );
7223
7224 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7225 assert_eq!(
7226 view.selections.display_ranges(cx),
7227 &[DisplayPoint::new(2, 8)..DisplayPoint::new(2, 8)]
7228 );
7229
7230 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7231 assert_eq!(
7232 view.selections.display_ranges(cx),
7233 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7234 );
7235
7236 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7237 assert_eq!(
7238 view.selections.display_ranges(cx),
7239 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
7240 );
7241 });
7242 }
7243
7244 #[gpui::test]
7245 fn test_delete_to_beginning_of_line(cx: &mut gpui::MutableAppContext) {
7246 cx.set_global(Settings::test(cx));
7247 let (text, ranges) = marked_text_ranges("one [two three] four");
7248 let buffer = MultiBuffer::build_simple(&text, cx);
7249
7250 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7251
7252 editor.update(cx, |editor, cx| {
7253 editor.change_selections(None, cx, |s| s.select_ranges(ranges));
7254 editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
7255 assert_eq!(editor.text(cx), " four");
7256 });
7257 }
7258
7259 #[gpui::test]
7260 fn test_delete_to_word_boundary(cx: &mut gpui::MutableAppContext) {
7261 cx.set_global(Settings::test(cx));
7262 let buffer = MultiBuffer::build_simple("one two three four", cx);
7263 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7264
7265 view.update(cx, |view, cx| {
7266 view.change_selections(None, cx, |s| {
7267 s.select_display_ranges([
7268 // an empty selection - the preceding word fragment is deleted
7269 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7270 // characters selected - they are deleted
7271 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 12),
7272 ])
7273 });
7274 view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
7275 });
7276
7277 assert_eq!(buffer.read(cx).read(cx).text(), "e two te four");
7278
7279 view.update(cx, |view, cx| {
7280 view.change_selections(None, cx, |s| {
7281 s.select_display_ranges([
7282 // an empty selection - the following word fragment is deleted
7283 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7284 // characters selected - they are deleted
7285 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 10),
7286 ])
7287 });
7288 view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
7289 });
7290
7291 assert_eq!(buffer.read(cx).read(cx).text(), "e t te our");
7292 }
7293
7294 #[gpui::test]
7295 fn test_newline(cx: &mut gpui::MutableAppContext) {
7296 cx.set_global(Settings::test(cx));
7297 let buffer = MultiBuffer::build_simple("aaaa\n bbbb\n", cx);
7298 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7299
7300 view.update(cx, |view, cx| {
7301 view.change_selections(None, cx, |s| {
7302 s.select_display_ranges([
7303 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7304 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7305 DisplayPoint::new(1, 6)..DisplayPoint::new(1, 6),
7306 ])
7307 });
7308
7309 view.newline(&Newline, cx);
7310 assert_eq!(view.text(cx), "aa\naa\n \n bb\n bb\n");
7311 });
7312 }
7313
7314 #[gpui::test]
7315 fn test_newline_with_old_selections(cx: &mut gpui::MutableAppContext) {
7316 cx.set_global(Settings::test(cx));
7317 let buffer = MultiBuffer::build_simple(
7318 "
7319 a
7320 b(
7321 X
7322 )
7323 c(
7324 X
7325 )
7326 "
7327 .unindent()
7328 .as_str(),
7329 cx,
7330 );
7331
7332 let (_, editor) = cx.add_window(Default::default(), |cx| {
7333 let mut editor = build_editor(buffer.clone(), cx);
7334 editor.change_selections(None, cx, |s| {
7335 s.select_ranges([
7336 Point::new(2, 4)..Point::new(2, 5),
7337 Point::new(5, 4)..Point::new(5, 5),
7338 ])
7339 });
7340 editor
7341 });
7342
7343 // Edit the buffer directly, deleting ranges surrounding the editor's selections
7344 buffer.update(cx, |buffer, cx| {
7345 buffer.edit(
7346 [
7347 (Point::new(1, 2)..Point::new(3, 0), ""),
7348 (Point::new(4, 2)..Point::new(6, 0), ""),
7349 ],
7350 cx,
7351 );
7352 assert_eq!(
7353 buffer.read(cx).text(),
7354 "
7355 a
7356 b()
7357 c()
7358 "
7359 .unindent()
7360 );
7361 });
7362
7363 editor.update(cx, |editor, cx| {
7364 assert_eq!(
7365 editor.selections.ranges(cx),
7366 &[
7367 Point::new(1, 2)..Point::new(1, 2),
7368 Point::new(2, 2)..Point::new(2, 2),
7369 ],
7370 );
7371
7372 editor.newline(&Newline, cx);
7373 assert_eq!(
7374 editor.text(cx),
7375 "
7376 a
7377 b(
7378 )
7379 c(
7380 )
7381 "
7382 .unindent()
7383 );
7384
7385 // The selections are moved after the inserted newlines
7386 assert_eq!(
7387 editor.selections.ranges(cx),
7388 &[
7389 Point::new(2, 0)..Point::new(2, 0),
7390 Point::new(4, 0)..Point::new(4, 0),
7391 ],
7392 );
7393 });
7394 }
7395
7396 #[gpui::test]
7397 fn test_insert_with_old_selections(cx: &mut gpui::MutableAppContext) {
7398 cx.set_global(Settings::test(cx));
7399 let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
7400 let (_, editor) = cx.add_window(Default::default(), |cx| {
7401 let mut editor = build_editor(buffer.clone(), cx);
7402 editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
7403 editor
7404 });
7405
7406 // Edit the buffer directly, deleting ranges surrounding the editor's selections
7407 buffer.update(cx, |buffer, cx| {
7408 buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], cx);
7409 assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
7410 });
7411
7412 editor.update(cx, |editor, cx| {
7413 assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
7414
7415 editor.insert("Z", cx);
7416 assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
7417
7418 // The selections are moved after the inserted characters
7419 assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
7420 });
7421 }
7422
7423 #[gpui::test]
7424 async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
7425 let mut cx = EditorTestContext::new(cx).await;
7426
7427 cx.set_state(indoc! {"
7428 [one} [two}
7429 three
7430 four"});
7431 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7432 cx.assert_editor_state(indoc! {"
7433 [one} [two}
7434 three
7435 four"});
7436
7437 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7438 cx.assert_editor_state(indoc! {"
7439 [one} [two}
7440 three
7441 four"});
7442
7443 // select across line ending
7444 cx.set_state(indoc! {"
7445 one two
7446 t[hree
7447 } four"});
7448 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7449 cx.assert_editor_state(indoc! {"
7450 one two
7451 t[hree
7452 } four"});
7453
7454 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7455 cx.assert_editor_state(indoc! {"
7456 one two
7457 t[hree
7458 } four"});
7459
7460 // Ensure that indenting/outdenting works when the cursor is at column 0.
7461 cx.set_state(indoc! {"
7462 one two
7463 |three
7464 four"});
7465 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7466 cx.assert_editor_state(indoc! {"
7467 one two
7468 |three
7469 four"});
7470
7471 cx.set_state(indoc! {"
7472 one two
7473 | three
7474 four"});
7475 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7476 cx.assert_editor_state(indoc! {"
7477 one two
7478 |three
7479 four"});
7480 }
7481
7482 #[gpui::test]
7483 async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
7484 let mut cx = EditorTestContext::new(cx).await;
7485 cx.update(|cx| {
7486 cx.update_global::<Settings, _, _>(|settings, _| {
7487 settings.hard_tabs = true;
7488 });
7489 });
7490
7491 // select two ranges on one line
7492 cx.set_state(indoc! {"
7493 [one} [two}
7494 three
7495 four"});
7496 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7497 cx.assert_editor_state(indoc! {"
7498 \t[one} [two}
7499 three
7500 four"});
7501 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7502 cx.assert_editor_state(indoc! {"
7503 \t\t[one} [two}
7504 three
7505 four"});
7506 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7507 cx.assert_editor_state(indoc! {"
7508 \t[one} [two}
7509 three
7510 four"});
7511 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7512 cx.assert_editor_state(indoc! {"
7513 [one} [two}
7514 three
7515 four"});
7516
7517 // select across a line ending
7518 cx.set_state(indoc! {"
7519 one two
7520 t[hree
7521 }four"});
7522 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7523 cx.assert_editor_state(indoc! {"
7524 one two
7525 \tt[hree
7526 }four"});
7527 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7528 cx.assert_editor_state(indoc! {"
7529 one two
7530 \t\tt[hree
7531 }four"});
7532 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7533 cx.assert_editor_state(indoc! {"
7534 one two
7535 \tt[hree
7536 }four"});
7537 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7538 cx.assert_editor_state(indoc! {"
7539 one two
7540 t[hree
7541 }four"});
7542
7543 // Ensure that indenting/outdenting works when the cursor is at column 0.
7544 cx.set_state(indoc! {"
7545 one two
7546 |three
7547 four"});
7548 cx.assert_editor_state(indoc! {"
7549 one two
7550 |three
7551 four"});
7552 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7553 cx.assert_editor_state(indoc! {"
7554 one two
7555 \t|three
7556 four"});
7557 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7558 cx.assert_editor_state(indoc! {"
7559 one two
7560 |three
7561 four"});
7562 }
7563
7564 #[gpui::test]
7565 fn test_indent_outdent_with_excerpts(cx: &mut gpui::MutableAppContext) {
7566 cx.set_global(
7567 Settings::test(cx)
7568 .with_overrides(
7569 "TOML",
7570 LanguageOverride {
7571 tab_size: Some(2),
7572 ..Default::default()
7573 },
7574 )
7575 .with_overrides(
7576 "Rust",
7577 LanguageOverride {
7578 tab_size: Some(4),
7579 ..Default::default()
7580 },
7581 ),
7582 );
7583 let toml_language = Arc::new(Language::new(
7584 LanguageConfig {
7585 name: "TOML".into(),
7586 ..Default::default()
7587 },
7588 None,
7589 ));
7590 let rust_language = Arc::new(Language::new(
7591 LanguageConfig {
7592 name: "Rust".into(),
7593 ..Default::default()
7594 },
7595 None,
7596 ));
7597
7598 let toml_buffer = cx
7599 .add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx).with_language(toml_language, cx));
7600 let rust_buffer = cx.add_model(|cx| {
7601 Buffer::new(0, "const c: usize = 3;\n", cx).with_language(rust_language, cx)
7602 });
7603 let multibuffer = cx.add_model(|cx| {
7604 let mut multibuffer = MultiBuffer::new(0);
7605 multibuffer.push_excerpts(
7606 toml_buffer.clone(),
7607 [ExcerptRange {
7608 context: Point::new(0, 0)..Point::new(2, 0),
7609 primary: None,
7610 }],
7611 cx,
7612 );
7613 multibuffer.push_excerpts(
7614 rust_buffer.clone(),
7615 [ExcerptRange {
7616 context: Point::new(0, 0)..Point::new(1, 0),
7617 primary: None,
7618 }],
7619 cx,
7620 );
7621 multibuffer
7622 });
7623
7624 cx.add_window(Default::default(), |cx| {
7625 let mut editor = build_editor(multibuffer, cx);
7626
7627 assert_eq!(
7628 editor.text(cx),
7629 indoc! {"
7630 a = 1
7631 b = 2
7632
7633 const c: usize = 3;
7634 "}
7635 );
7636
7637 select_ranges(
7638 &mut editor,
7639 indoc! {"
7640 [a] = 1
7641 b = 2
7642
7643 [const c:] usize = 3;
7644 "},
7645 cx,
7646 );
7647
7648 editor.tab(&Tab, cx);
7649 assert_text_with_selections(
7650 &mut editor,
7651 indoc! {"
7652 [a] = 1
7653 b = 2
7654
7655 [const c:] usize = 3;
7656 "},
7657 cx,
7658 );
7659 editor.tab_prev(&TabPrev, cx);
7660 assert_text_with_selections(
7661 &mut editor,
7662 indoc! {"
7663 [a] = 1
7664 b = 2
7665
7666 [const c:] usize = 3;
7667 "},
7668 cx,
7669 );
7670
7671 editor
7672 });
7673 }
7674
7675 #[gpui::test]
7676 async fn test_backspace(cx: &mut gpui::TestAppContext) {
7677 let mut cx = EditorTestContext::new(cx).await;
7678 // Basic backspace
7679 cx.set_state(indoc! {"
7680 on|e two three
7681 fou[r} five six
7682 seven {eight nine
7683 ]ten"});
7684 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
7685 cx.assert_editor_state(indoc! {"
7686 o|e two three
7687 fou| five six
7688 seven |ten"});
7689
7690 // Test backspace inside and around indents
7691 cx.set_state(indoc! {"
7692 zero
7693 |one
7694 |two
7695 | | | three
7696 | | four"});
7697 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
7698 cx.assert_editor_state(indoc! {"
7699 zero
7700 |one
7701 |two
7702 | three| four"});
7703
7704 // Test backspace with line_mode set to true
7705 cx.update_editor(|e, _| e.selections.line_mode = true);
7706 cx.set_state(indoc! {"
7707 The |quick |brown
7708 fox jumps over
7709 the lazy dog
7710 |The qu[ick b}rown"});
7711 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
7712 cx.assert_editor_state(indoc! {"
7713 |fox jumps over
7714 the lazy dog|"});
7715 }
7716
7717 #[gpui::test]
7718 async fn test_delete(cx: &mut gpui::TestAppContext) {
7719 let mut cx = EditorTestContext::new(cx).await;
7720
7721 cx.set_state(indoc! {"
7722 on|e two three
7723 fou[r} five six
7724 seven {eight nine
7725 ]ten"});
7726 cx.update_editor(|e, cx| e.delete(&Delete, cx));
7727 cx.assert_editor_state(indoc! {"
7728 on| two three
7729 fou| five six
7730 seven |ten"});
7731
7732 // Test backspace with line_mode set to true
7733 cx.update_editor(|e, _| e.selections.line_mode = true);
7734 cx.set_state(indoc! {"
7735 The |quick |brown
7736 fox {jum]ps over
7737 the lazy dog
7738 |The qu[ick b}rown"});
7739 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
7740 cx.assert_editor_state("|the lazy dog|");
7741 }
7742
7743 #[gpui::test]
7744 fn test_delete_line(cx: &mut gpui::MutableAppContext) {
7745 cx.set_global(Settings::test(cx));
7746 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7747 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7748 view.update(cx, |view, cx| {
7749 view.change_selections(None, cx, |s| {
7750 s.select_display_ranges([
7751 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7752 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
7753 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
7754 ])
7755 });
7756 view.delete_line(&DeleteLine, cx);
7757 assert_eq!(view.display_text(cx), "ghi");
7758 assert_eq!(
7759 view.selections.display_ranges(cx),
7760 vec![
7761 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7762 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
7763 ]
7764 );
7765 });
7766
7767 cx.set_global(Settings::test(cx));
7768 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7769 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7770 view.update(cx, |view, cx| {
7771 view.change_selections(None, cx, |s| {
7772 s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)])
7773 });
7774 view.delete_line(&DeleteLine, cx);
7775 assert_eq!(view.display_text(cx), "ghi\n");
7776 assert_eq!(
7777 view.selections.display_ranges(cx),
7778 vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
7779 );
7780 });
7781 }
7782
7783 #[gpui::test]
7784 fn test_duplicate_line(cx: &mut gpui::MutableAppContext) {
7785 cx.set_global(Settings::test(cx));
7786 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7787 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7788 view.update(cx, |view, cx| {
7789 view.change_selections(None, cx, |s| {
7790 s.select_display_ranges([
7791 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
7792 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7793 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7794 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
7795 ])
7796 });
7797 view.duplicate_line(&DuplicateLine, cx);
7798 assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
7799 assert_eq!(
7800 view.selections.display_ranges(cx),
7801 vec![
7802 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
7803 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7804 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
7805 DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
7806 ]
7807 );
7808 });
7809
7810 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7811 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7812 view.update(cx, |view, cx| {
7813 view.change_selections(None, cx, |s| {
7814 s.select_display_ranges([
7815 DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
7816 DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
7817 ])
7818 });
7819 view.duplicate_line(&DuplicateLine, cx);
7820 assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
7821 assert_eq!(
7822 view.selections.display_ranges(cx),
7823 vec![
7824 DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
7825 DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
7826 ]
7827 );
7828 });
7829 }
7830
7831 #[gpui::test]
7832 fn test_move_line_up_down(cx: &mut gpui::MutableAppContext) {
7833 cx.set_global(Settings::test(cx));
7834 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
7835 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7836 view.update(cx, |view, cx| {
7837 view.fold_ranges(
7838 vec![
7839 Point::new(0, 2)..Point::new(1, 2),
7840 Point::new(2, 3)..Point::new(4, 1),
7841 Point::new(7, 0)..Point::new(8, 4),
7842 ],
7843 cx,
7844 );
7845 view.change_selections(None, cx, |s| {
7846 s.select_display_ranges([
7847 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7848 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
7849 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
7850 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
7851 ])
7852 });
7853 assert_eq!(
7854 view.display_text(cx),
7855 "aa…bbb\nccc…eeee\nfffff\nggggg\n…i\njjjjj"
7856 );
7857
7858 view.move_line_up(&MoveLineUp, cx);
7859 assert_eq!(
7860 view.display_text(cx),
7861 "aa…bbb\nccc…eeee\nggggg\n…i\njjjjj\nfffff"
7862 );
7863 assert_eq!(
7864 view.selections.display_ranges(cx),
7865 vec![
7866 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7867 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
7868 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
7869 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
7870 ]
7871 );
7872 });
7873
7874 view.update(cx, |view, cx| {
7875 view.move_line_down(&MoveLineDown, cx);
7876 assert_eq!(
7877 view.display_text(cx),
7878 "ccc…eeee\naa…bbb\nfffff\nggggg\n…i\njjjjj"
7879 );
7880 assert_eq!(
7881 view.selections.display_ranges(cx),
7882 vec![
7883 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
7884 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
7885 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
7886 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
7887 ]
7888 );
7889 });
7890
7891 view.update(cx, |view, cx| {
7892 view.move_line_down(&MoveLineDown, cx);
7893 assert_eq!(
7894 view.display_text(cx),
7895 "ccc…eeee\nfffff\naa…bbb\nggggg\n…i\njjjjj"
7896 );
7897 assert_eq!(
7898 view.selections.display_ranges(cx),
7899 vec![
7900 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
7901 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
7902 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
7903 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
7904 ]
7905 );
7906 });
7907
7908 view.update(cx, |view, cx| {
7909 view.move_line_up(&MoveLineUp, cx);
7910 assert_eq!(
7911 view.display_text(cx),
7912 "ccc…eeee\naa…bbb\nggggg\n…i\njjjjj\nfffff"
7913 );
7914 assert_eq!(
7915 view.selections.display_ranges(cx),
7916 vec![
7917 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
7918 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
7919 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
7920 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
7921 ]
7922 );
7923 });
7924 }
7925
7926 #[gpui::test]
7927 fn test_move_line_up_down_with_blocks(cx: &mut gpui::MutableAppContext) {
7928 cx.set_global(Settings::test(cx));
7929 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
7930 let snapshot = buffer.read(cx).snapshot(cx);
7931 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7932 editor.update(cx, |editor, cx| {
7933 editor.insert_blocks(
7934 [BlockProperties {
7935 style: BlockStyle::Fixed,
7936 position: snapshot.anchor_after(Point::new(2, 0)),
7937 disposition: BlockDisposition::Below,
7938 height: 1,
7939 render: Arc::new(|_| Empty::new().boxed()),
7940 }],
7941 cx,
7942 );
7943 editor.change_selections(None, cx, |s| {
7944 s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
7945 });
7946 editor.move_line_down(&MoveLineDown, cx);
7947 });
7948 }
7949
7950 #[gpui::test]
7951 fn test_transpose(cx: &mut gpui::MutableAppContext) {
7952 cx.set_global(Settings::test(cx));
7953
7954 cx.add_window(Default::default(), |cx| {
7955 let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
7956
7957 editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
7958 editor.transpose(&Default::default(), cx);
7959 assert_eq!(editor.text(cx), "bac");
7960 assert_eq!(editor.selections.ranges(cx), [2..2]);
7961
7962 editor.transpose(&Default::default(), cx);
7963 assert_eq!(editor.text(cx), "bca");
7964 assert_eq!(editor.selections.ranges(cx), [3..3]);
7965
7966 editor.transpose(&Default::default(), cx);
7967 assert_eq!(editor.text(cx), "bac");
7968 assert_eq!(editor.selections.ranges(cx), [3..3]);
7969
7970 editor
7971 })
7972 .1;
7973
7974 cx.add_window(Default::default(), |cx| {
7975 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
7976
7977 editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
7978 editor.transpose(&Default::default(), cx);
7979 assert_eq!(editor.text(cx), "acb\nde");
7980 assert_eq!(editor.selections.ranges(cx), [3..3]);
7981
7982 editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
7983 editor.transpose(&Default::default(), cx);
7984 assert_eq!(editor.text(cx), "acbd\ne");
7985 assert_eq!(editor.selections.ranges(cx), [5..5]);
7986
7987 editor.transpose(&Default::default(), cx);
7988 assert_eq!(editor.text(cx), "acbde\n");
7989 assert_eq!(editor.selections.ranges(cx), [6..6]);
7990
7991 editor.transpose(&Default::default(), cx);
7992 assert_eq!(editor.text(cx), "acbd\ne");
7993 assert_eq!(editor.selections.ranges(cx), [6..6]);
7994
7995 editor
7996 })
7997 .1;
7998
7999 cx.add_window(Default::default(), |cx| {
8000 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
8001
8002 editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
8003 editor.transpose(&Default::default(), cx);
8004 assert_eq!(editor.text(cx), "bacd\ne");
8005 assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
8006
8007 editor.transpose(&Default::default(), cx);
8008 assert_eq!(editor.text(cx), "bcade\n");
8009 assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
8010
8011 editor.transpose(&Default::default(), cx);
8012 assert_eq!(editor.text(cx), "bcda\ne");
8013 assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
8014
8015 editor.transpose(&Default::default(), cx);
8016 assert_eq!(editor.text(cx), "bcade\n");
8017 assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
8018
8019 editor.transpose(&Default::default(), cx);
8020 assert_eq!(editor.text(cx), "bcaed\n");
8021 assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
8022
8023 editor
8024 })
8025 .1;
8026
8027 cx.add_window(Default::default(), |cx| {
8028 let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
8029
8030 editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
8031 editor.transpose(&Default::default(), cx);
8032 assert_eq!(editor.text(cx), "🏀🍐✋");
8033 assert_eq!(editor.selections.ranges(cx), [8..8]);
8034
8035 editor.transpose(&Default::default(), cx);
8036 assert_eq!(editor.text(cx), "🏀✋🍐");
8037 assert_eq!(editor.selections.ranges(cx), [11..11]);
8038
8039 editor.transpose(&Default::default(), cx);
8040 assert_eq!(editor.text(cx), "🏀🍐✋");
8041 assert_eq!(editor.selections.ranges(cx), [11..11]);
8042
8043 editor
8044 })
8045 .1;
8046 }
8047
8048 #[gpui::test]
8049 async fn test_clipboard(cx: &mut gpui::TestAppContext) {
8050 let mut cx = EditorTestContext::new(cx).await;
8051
8052 cx.set_state("[one✅ }two [three }four [five }six ");
8053 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8054 cx.assert_editor_state("|two |four |six ");
8055
8056 // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
8057 cx.set_state("two |four |six |");
8058 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8059 cx.assert_editor_state("two one✅ |four three |six five |");
8060
8061 // Paste again but with only two cursors. Since the number of cursors doesn't
8062 // match the number of slices in the clipboard, the entire clipboard text
8063 // is pasted at each cursor.
8064 cx.set_state("|two one✅ four three six five |");
8065 cx.update_editor(|e, cx| {
8066 e.handle_input(&Input("( ".into()), cx);
8067 e.paste(&Paste, cx);
8068 e.handle_input(&Input(") ".into()), cx);
8069 });
8070 cx.assert_editor_state(indoc! {"
8071 ( one✅
8072 three
8073 five ) |two one✅ four three six five ( one✅
8074 three
8075 five ) |"});
8076
8077 // Cut with three selections, one of which is full-line.
8078 cx.set_state(indoc! {"
8079 1[2}3
8080 4|567
8081 [8}9"});
8082 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8083 cx.assert_editor_state(indoc! {"
8084 1|3
8085 |9"});
8086
8087 // Paste with three selections, noticing how the copied selection that was full-line
8088 // gets inserted before the second cursor.
8089 cx.set_state(indoc! {"
8090 1|3
8091 9|
8092 [o}ne"});
8093 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8094 cx.assert_editor_state(indoc! {"
8095 12|3
8096 4567
8097 9|
8098 8|ne"});
8099
8100 // Copy with a single cursor only, which writes the whole line into the clipboard.
8101 cx.set_state(indoc! {"
8102 The quick brown
8103 fox ju|mps over
8104 the lazy dog"});
8105 cx.update_editor(|e, cx| e.copy(&Copy, cx));
8106 cx.assert_clipboard_content(Some("fox jumps over\n"));
8107
8108 // Paste with three selections, noticing how the copied full-line selection is inserted
8109 // before the empty selections but replaces the selection that is non-empty.
8110 cx.set_state(indoc! {"
8111 T|he quick brown
8112 [fo}x jumps over
8113 t|he lazy dog"});
8114 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8115 cx.assert_editor_state(indoc! {"
8116 fox jumps over
8117 T|he quick brown
8118 fox jumps over
8119 |x jumps over
8120 fox jumps over
8121 t|he lazy dog"});
8122 }
8123
8124 #[gpui::test]
8125 fn test_select_all(cx: &mut gpui::MutableAppContext) {
8126 cx.set_global(Settings::test(cx));
8127 let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
8128 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8129 view.update(cx, |view, cx| {
8130 view.select_all(&SelectAll, cx);
8131 assert_eq!(
8132 view.selections.display_ranges(cx),
8133 &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
8134 );
8135 });
8136 }
8137
8138 #[gpui::test]
8139 fn test_select_line(cx: &mut gpui::MutableAppContext) {
8140 cx.set_global(Settings::test(cx));
8141 let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
8142 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8143 view.update(cx, |view, cx| {
8144 view.change_selections(None, cx, |s| {
8145 s.select_display_ranges([
8146 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8147 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8148 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8149 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
8150 ])
8151 });
8152 view.select_line(&SelectLine, cx);
8153 assert_eq!(
8154 view.selections.display_ranges(cx),
8155 vec![
8156 DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
8157 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
8158 ]
8159 );
8160 });
8161
8162 view.update(cx, |view, cx| {
8163 view.select_line(&SelectLine, cx);
8164 assert_eq!(
8165 view.selections.display_ranges(cx),
8166 vec![
8167 DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
8168 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
8169 ]
8170 );
8171 });
8172
8173 view.update(cx, |view, cx| {
8174 view.select_line(&SelectLine, cx);
8175 assert_eq!(
8176 view.selections.display_ranges(cx),
8177 vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
8178 );
8179 });
8180 }
8181
8182 #[gpui::test]
8183 fn test_split_selection_into_lines(cx: &mut gpui::MutableAppContext) {
8184 cx.set_global(Settings::test(cx));
8185 let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
8186 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8187 view.update(cx, |view, cx| {
8188 view.fold_ranges(
8189 vec![
8190 Point::new(0, 2)..Point::new(1, 2),
8191 Point::new(2, 3)..Point::new(4, 1),
8192 Point::new(7, 0)..Point::new(8, 4),
8193 ],
8194 cx,
8195 );
8196 view.change_selections(None, cx, |s| {
8197 s.select_display_ranges([
8198 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8199 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8200 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8201 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
8202 ])
8203 });
8204 assert_eq!(view.display_text(cx), "aa…bbb\nccc…eeee\nfffff\nggggg\n…i");
8205 });
8206
8207 view.update(cx, |view, cx| {
8208 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
8209 assert_eq!(
8210 view.display_text(cx),
8211 "aaaaa\nbbbbb\nccc…eeee\nfffff\nggggg\n…i"
8212 );
8213 assert_eq!(
8214 view.selections.display_ranges(cx),
8215 [
8216 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8217 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8218 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
8219 DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
8220 ]
8221 );
8222 });
8223
8224 view.update(cx, |view, cx| {
8225 view.change_selections(None, cx, |s| {
8226 s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
8227 });
8228 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
8229 assert_eq!(
8230 view.display_text(cx),
8231 "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
8232 );
8233 assert_eq!(
8234 view.selections.display_ranges(cx),
8235 [
8236 DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
8237 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
8238 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
8239 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
8240 DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
8241 DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
8242 DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
8243 DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
8244 ]
8245 );
8246 });
8247 }
8248
8249 #[gpui::test]
8250 fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) {
8251 cx.set_global(Settings::test(cx));
8252 let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
8253 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8254
8255 view.update(cx, |view, cx| {
8256 view.change_selections(None, cx, |s| {
8257 s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)])
8258 });
8259 });
8260 view.update(cx, |view, cx| {
8261 view.add_selection_above(&AddSelectionAbove, cx);
8262 assert_eq!(
8263 view.selections.display_ranges(cx),
8264 vec![
8265 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8266 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8267 ]
8268 );
8269 });
8270
8271 view.update(cx, |view, cx| {
8272 view.add_selection_above(&AddSelectionAbove, cx);
8273 assert_eq!(
8274 view.selections.display_ranges(cx),
8275 vec![
8276 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8277 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8278 ]
8279 );
8280 });
8281
8282 view.update(cx, |view, cx| {
8283 view.add_selection_below(&AddSelectionBelow, cx);
8284 assert_eq!(
8285 view.selections.display_ranges(cx),
8286 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
8287 );
8288
8289 view.undo_selection(&UndoSelection, cx);
8290 assert_eq!(
8291 view.selections.display_ranges(cx),
8292 vec![
8293 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8294 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8295 ]
8296 );
8297
8298 view.redo_selection(&RedoSelection, cx);
8299 assert_eq!(
8300 view.selections.display_ranges(cx),
8301 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
8302 );
8303 });
8304
8305 view.update(cx, |view, cx| {
8306 view.add_selection_below(&AddSelectionBelow, cx);
8307 assert_eq!(
8308 view.selections.display_ranges(cx),
8309 vec![
8310 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
8311 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
8312 ]
8313 );
8314 });
8315
8316 view.update(cx, |view, cx| {
8317 view.add_selection_below(&AddSelectionBelow, cx);
8318 assert_eq!(
8319 view.selections.display_ranges(cx),
8320 vec![
8321 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
8322 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
8323 ]
8324 );
8325 });
8326
8327 view.update(cx, |view, cx| {
8328 view.change_selections(None, cx, |s| {
8329 s.select_display_ranges([DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)])
8330 });
8331 });
8332 view.update(cx, |view, cx| {
8333 view.add_selection_below(&AddSelectionBelow, cx);
8334 assert_eq!(
8335 view.selections.display_ranges(cx),
8336 vec![
8337 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
8338 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
8339 ]
8340 );
8341 });
8342
8343 view.update(cx, |view, cx| {
8344 view.add_selection_below(&AddSelectionBelow, cx);
8345 assert_eq!(
8346 view.selections.display_ranges(cx),
8347 vec![
8348 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
8349 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
8350 ]
8351 );
8352 });
8353
8354 view.update(cx, |view, cx| {
8355 view.add_selection_above(&AddSelectionAbove, cx);
8356 assert_eq!(
8357 view.selections.display_ranges(cx),
8358 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
8359 );
8360 });
8361
8362 view.update(cx, |view, cx| {
8363 view.add_selection_above(&AddSelectionAbove, cx);
8364 assert_eq!(
8365 view.selections.display_ranges(cx),
8366 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
8367 );
8368 });
8369
8370 view.update(cx, |view, cx| {
8371 view.change_selections(None, cx, |s| {
8372 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)])
8373 });
8374 view.add_selection_below(&AddSelectionBelow, cx);
8375 assert_eq!(
8376 view.selections.display_ranges(cx),
8377 vec![
8378 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8379 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8380 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8381 ]
8382 );
8383 });
8384
8385 view.update(cx, |view, cx| {
8386 view.add_selection_below(&AddSelectionBelow, cx);
8387 assert_eq!(
8388 view.selections.display_ranges(cx),
8389 vec![
8390 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8391 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8392 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8393 DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
8394 ]
8395 );
8396 });
8397
8398 view.update(cx, |view, cx| {
8399 view.add_selection_above(&AddSelectionAbove, cx);
8400 assert_eq!(
8401 view.selections.display_ranges(cx),
8402 vec![
8403 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8404 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8405 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8406 ]
8407 );
8408 });
8409
8410 view.update(cx, |view, cx| {
8411 view.change_selections(None, cx, |s| {
8412 s.select_display_ranges([DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)])
8413 });
8414 });
8415 view.update(cx, |view, cx| {
8416 view.add_selection_above(&AddSelectionAbove, cx);
8417 assert_eq!(
8418 view.selections.display_ranges(cx),
8419 vec![
8420 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
8421 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
8422 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
8423 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
8424 ]
8425 );
8426 });
8427
8428 view.update(cx, |view, cx| {
8429 view.add_selection_below(&AddSelectionBelow, cx);
8430 assert_eq!(
8431 view.selections.display_ranges(cx),
8432 vec![
8433 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
8434 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
8435 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
8436 ]
8437 );
8438 });
8439 }
8440
8441 #[gpui::test]
8442 fn test_select_next(cx: &mut gpui::MutableAppContext) {
8443 cx.set_global(Settings::test(cx));
8444
8445 let (text, ranges) = marked_text_ranges("[abc]\n[abc] [abc]\ndefabc\n[abc]");
8446 let buffer = MultiBuffer::build_simple(&text, cx);
8447 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8448
8449 view.update(cx, |view, cx| {
8450 view.change_selections(None, cx, |s| {
8451 s.select_ranges([ranges[1].start + 1..ranges[1].start + 1])
8452 });
8453 view.select_next(
8454 &SelectNext {
8455 replace_newest: false,
8456 },
8457 cx,
8458 );
8459 assert_eq!(view.selections.ranges(cx), &ranges[1..2]);
8460
8461 view.select_next(
8462 &SelectNext {
8463 replace_newest: false,
8464 },
8465 cx,
8466 );
8467 assert_eq!(view.selections.ranges(cx), &ranges[1..3]);
8468
8469 view.undo_selection(&UndoSelection, cx);
8470 assert_eq!(view.selections.ranges(cx), &ranges[1..2]);
8471
8472 view.redo_selection(&RedoSelection, cx);
8473 assert_eq!(view.selections.ranges(cx), &ranges[1..3]);
8474
8475 view.select_next(
8476 &SelectNext {
8477 replace_newest: false,
8478 },
8479 cx,
8480 );
8481 assert_eq!(view.selections.ranges(cx), &ranges[1..4]);
8482
8483 view.select_next(
8484 &SelectNext {
8485 replace_newest: false,
8486 },
8487 cx,
8488 );
8489 assert_eq!(view.selections.ranges(cx), &ranges[0..4]);
8490 });
8491 }
8492
8493 #[gpui::test]
8494 async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
8495 cx.update(|cx| cx.set_global(Settings::test(cx)));
8496 let language = Arc::new(Language::new(
8497 LanguageConfig::default(),
8498 Some(tree_sitter_rust::language()),
8499 ));
8500
8501 let text = r#"
8502 use mod1::mod2::{mod3, mod4};
8503
8504 fn fn_1(param1: bool, param2: &str) {
8505 let var1 = "text";
8506 }
8507 "#
8508 .unindent();
8509
8510 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8511 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8512 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
8513 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
8514 .await;
8515
8516 view.update(cx, |view, cx| {
8517 view.change_selections(None, cx, |s| {
8518 s.select_display_ranges([
8519 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8520 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8521 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8522 ]);
8523 });
8524 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8525 });
8526 assert_eq!(
8527 view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
8528 &[
8529 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
8530 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8531 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
8532 ]
8533 );
8534
8535 view.update(cx, |view, cx| {
8536 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8537 });
8538 assert_eq!(
8539 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8540 &[
8541 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
8542 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
8543 ]
8544 );
8545
8546 view.update(cx, |view, cx| {
8547 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8548 });
8549 assert_eq!(
8550 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8551 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
8552 );
8553
8554 // Trying to expand the selected syntax node one more time has no effect.
8555 view.update(cx, |view, cx| {
8556 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8557 });
8558 assert_eq!(
8559 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8560 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
8561 );
8562
8563 view.update(cx, |view, cx| {
8564 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8565 });
8566 assert_eq!(
8567 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8568 &[
8569 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
8570 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
8571 ]
8572 );
8573
8574 view.update(cx, |view, cx| {
8575 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8576 });
8577 assert_eq!(
8578 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8579 &[
8580 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
8581 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8582 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
8583 ]
8584 );
8585
8586 view.update(cx, |view, cx| {
8587 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8588 });
8589 assert_eq!(
8590 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8591 &[
8592 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8593 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8594 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8595 ]
8596 );
8597
8598 // Trying to shrink the selected syntax node one more time has no effect.
8599 view.update(cx, |view, cx| {
8600 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8601 });
8602 assert_eq!(
8603 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8604 &[
8605 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8606 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8607 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8608 ]
8609 );
8610
8611 // Ensure that we keep expanding the selection if the larger selection starts or ends within
8612 // a fold.
8613 view.update(cx, |view, cx| {
8614 view.fold_ranges(
8615 vec![
8616 Point::new(0, 21)..Point::new(0, 24),
8617 Point::new(3, 20)..Point::new(3, 22),
8618 ],
8619 cx,
8620 );
8621 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8622 });
8623 assert_eq!(
8624 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8625 &[
8626 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
8627 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8628 DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
8629 ]
8630 );
8631 }
8632
8633 #[gpui::test]
8634 async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
8635 cx.update(|cx| cx.set_global(Settings::test(cx)));
8636 let language = Arc::new(
8637 Language::new(
8638 LanguageConfig {
8639 brackets: vec![
8640 BracketPair {
8641 start: "{".to_string(),
8642 end: "}".to_string(),
8643 close: false,
8644 newline: true,
8645 },
8646 BracketPair {
8647 start: "(".to_string(),
8648 end: ")".to_string(),
8649 close: false,
8650 newline: true,
8651 },
8652 ],
8653 ..Default::default()
8654 },
8655 Some(tree_sitter_rust::language()),
8656 )
8657 .with_indents_query(
8658 r#"
8659 (_ "(" ")" @end) @indent
8660 (_ "{" "}" @end) @indent
8661 "#,
8662 )
8663 .unwrap(),
8664 );
8665
8666 let text = "fn a() {}";
8667
8668 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8669 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8670 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
8671 editor
8672 .condition(&cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
8673 .await;
8674
8675 editor.update(cx, |editor, cx| {
8676 editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
8677 editor.newline(&Newline, cx);
8678 assert_eq!(editor.text(cx), "fn a(\n \n) {\n \n}\n");
8679 assert_eq!(
8680 editor.selections.ranges(cx),
8681 &[
8682 Point::new(1, 4)..Point::new(1, 4),
8683 Point::new(3, 4)..Point::new(3, 4),
8684 Point::new(5, 0)..Point::new(5, 0)
8685 ]
8686 );
8687 });
8688 }
8689
8690 #[gpui::test]
8691 async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
8692 cx.update(|cx| cx.set_global(Settings::test(cx)));
8693 let language = Arc::new(Language::new(
8694 LanguageConfig {
8695 brackets: vec![
8696 BracketPair {
8697 start: "{".to_string(),
8698 end: "}".to_string(),
8699 close: true,
8700 newline: true,
8701 },
8702 BracketPair {
8703 start: "/*".to_string(),
8704 end: " */".to_string(),
8705 close: true,
8706 newline: true,
8707 },
8708 ],
8709 autoclose_before: "})]".to_string(),
8710 ..Default::default()
8711 },
8712 Some(tree_sitter_rust::language()),
8713 ));
8714
8715 let text = r#"
8716 a
8717
8718 /
8719
8720 "#
8721 .unindent();
8722
8723 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8724 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8725 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
8726 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
8727 .await;
8728
8729 view.update(cx, |view, cx| {
8730 view.change_selections(None, cx, |s| {
8731 s.select_display_ranges([
8732 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8733 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8734 ])
8735 });
8736
8737 view.handle_input(&Input("{".to_string()), cx);
8738 view.handle_input(&Input("{".to_string()), cx);
8739 view.handle_input(&Input("{".to_string()), cx);
8740 assert_eq!(
8741 view.text(cx),
8742 "
8743 {{{}}}
8744 {{{}}}
8745 /
8746
8747 "
8748 .unindent()
8749 );
8750
8751 view.move_right(&MoveRight, cx);
8752 view.handle_input(&Input("}".to_string()), cx);
8753 view.handle_input(&Input("}".to_string()), cx);
8754 view.handle_input(&Input("}".to_string()), cx);
8755 assert_eq!(
8756 view.text(cx),
8757 "
8758 {{{}}}}
8759 {{{}}}}
8760 /
8761
8762 "
8763 .unindent()
8764 );
8765
8766 view.undo(&Undo, 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.change_selections(None, cx, |s| {
8782 s.select_display_ranges([
8783 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8784 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8785 ])
8786 });
8787 view.handle_input(&Input("*".to_string()), cx);
8788 assert_eq!(
8789 view.text(cx),
8790 "
8791 a
8792
8793 /*
8794 *
8795 "
8796 .unindent()
8797 );
8798
8799 // Don't autoclose if the next character isn't whitespace and isn't
8800 // listed in the language's "autoclose_before" section.
8801 view.finalize_last_transaction(cx);
8802 view.change_selections(None, cx, |s| {
8803 s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)])
8804 });
8805 view.handle_input(&Input("{".to_string()), cx);
8806 assert_eq!(
8807 view.text(cx),
8808 "
8809 {a
8810
8811 /*
8812 *
8813 "
8814 .unindent()
8815 );
8816
8817 view.undo(&Undo, cx);
8818 view.change_selections(None, cx, |s| {
8819 s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1)])
8820 });
8821 view.handle_input(&Input("{".to_string()), cx);
8822 assert_eq!(
8823 view.text(cx),
8824 "
8825 {a}
8826
8827 /*
8828 *
8829 "
8830 .unindent()
8831 );
8832 assert_eq!(
8833 view.selections.display_ranges(cx),
8834 [DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]
8835 );
8836 });
8837 }
8838
8839 #[gpui::test]
8840 async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
8841 cx.update(|cx| cx.set_global(Settings::test(cx)));
8842 let language = Arc::new(Language::new(
8843 LanguageConfig {
8844 brackets: vec![BracketPair {
8845 start: "{".to_string(),
8846 end: "}".to_string(),
8847 close: true,
8848 newline: true,
8849 }],
8850 ..Default::default()
8851 },
8852 Some(tree_sitter_rust::language()),
8853 ));
8854
8855 let text = r#"
8856 a
8857 b
8858 c
8859 "#
8860 .unindent();
8861
8862 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8863 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8864 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
8865 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
8866 .await;
8867
8868 view.update(cx, |view, cx| {
8869 view.change_selections(None, cx, |s| {
8870 s.select_display_ranges([
8871 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8872 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
8873 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
8874 ])
8875 });
8876
8877 view.handle_input(&Input("{".to_string()), cx);
8878 view.handle_input(&Input("{".to_string()), cx);
8879 view.handle_input(&Input("{".to_string()), cx);
8880 assert_eq!(
8881 view.text(cx),
8882 "
8883 {{{a}}}
8884 {{{b}}}
8885 {{{c}}}
8886 "
8887 .unindent()
8888 );
8889 assert_eq!(
8890 view.selections.display_ranges(cx),
8891 [
8892 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
8893 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
8894 DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
8895 ]
8896 );
8897
8898 view.undo(&Undo, cx);
8899 assert_eq!(
8900 view.text(cx),
8901 "
8902 a
8903 b
8904 c
8905 "
8906 .unindent()
8907 );
8908 assert_eq!(
8909 view.selections.display_ranges(cx),
8910 [
8911 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8912 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
8913 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
8914 ]
8915 );
8916 });
8917 }
8918
8919 #[gpui::test]
8920 async fn test_snippets(cx: &mut gpui::TestAppContext) {
8921 cx.update(|cx| cx.set_global(Settings::test(cx)));
8922
8923 let (text, insertion_ranges) = marked_text_ranges(indoc! {"
8924 a.| b
8925 a.| b
8926 a.| b"});
8927 let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
8928 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
8929
8930 editor.update(cx, |editor, cx| {
8931 let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
8932
8933 editor
8934 .insert_snippet(&insertion_ranges, snippet, cx)
8935 .unwrap();
8936
8937 fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text_ranges: &str) {
8938 let range_markers = ('<', '>');
8939 let (expected_text, mut selection_ranges_lookup) =
8940 marked_text_ranges_by(marked_text_ranges, vec![range_markers.clone().into()]);
8941 let selection_ranges = selection_ranges_lookup
8942 .remove(&range_markers.into())
8943 .unwrap();
8944 assert_eq!(editor.text(cx), expected_text);
8945 assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
8946 }
8947 assert(
8948 editor,
8949 cx,
8950 indoc! {"
8951 a.f(<one>, two, <three>) b
8952 a.f(<one>, two, <three>) b
8953 a.f(<one>, two, <three>) b"},
8954 );
8955
8956 // Can't move earlier than the first tab stop
8957 assert!(!editor.move_to_prev_snippet_tabstop(cx));
8958 assert(
8959 editor,
8960 cx,
8961 indoc! {"
8962 a.f(<one>, two, <three>) b
8963 a.f(<one>, two, <three>) b
8964 a.f(<one>, two, <three>) b"},
8965 );
8966
8967 assert!(editor.move_to_next_snippet_tabstop(cx));
8968 assert(
8969 editor,
8970 cx,
8971 indoc! {"
8972 a.f(one, <two>, three) b
8973 a.f(one, <two>, three) b
8974 a.f(one, <two>, three) b"},
8975 );
8976
8977 editor.move_to_prev_snippet_tabstop(cx);
8978 assert(
8979 editor,
8980 cx,
8981 indoc! {"
8982 a.f(<one>, two, <three>) b
8983 a.f(<one>, two, <three>) b
8984 a.f(<one>, two, <three>) b"},
8985 );
8986
8987 assert!(editor.move_to_next_snippet_tabstop(cx));
8988 assert(
8989 editor,
8990 cx,
8991 indoc! {"
8992 a.f(one, <two>, three) b
8993 a.f(one, <two>, three) b
8994 a.f(one, <two>, three) b"},
8995 );
8996 assert!(editor.move_to_next_snippet_tabstop(cx));
8997 assert(
8998 editor,
8999 cx,
9000 indoc! {"
9001 a.f(one, two, three)<> b
9002 a.f(one, two, three)<> b
9003 a.f(one, two, three)<> b"},
9004 );
9005
9006 // As soon as the last tab stop is reached, snippet state is gone
9007 editor.move_to_prev_snippet_tabstop(cx);
9008 assert(
9009 editor,
9010 cx,
9011 indoc! {"
9012 a.f(one, two, three)<> b
9013 a.f(one, two, three)<> b
9014 a.f(one, two, three)<> b"},
9015 );
9016 });
9017 }
9018
9019 #[gpui::test]
9020 async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
9021 cx.foreground().forbid_parking();
9022
9023 let mut language = Language::new(
9024 LanguageConfig {
9025 name: "Rust".into(),
9026 path_suffixes: vec!["rs".to_string()],
9027 ..Default::default()
9028 },
9029 Some(tree_sitter_rust::language()),
9030 );
9031 let mut fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
9032 capabilities: lsp::ServerCapabilities {
9033 document_formatting_provider: Some(lsp::OneOf::Left(true)),
9034 ..Default::default()
9035 },
9036 ..Default::default()
9037 });
9038
9039 let fs = FakeFs::new(cx.background().clone());
9040 fs.insert_file("/file.rs", Default::default()).await;
9041
9042 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
9043 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
9044 let buffer = project
9045 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
9046 .await
9047 .unwrap();
9048
9049 cx.foreground().start_waiting();
9050 let fake_server = fake_servers.next().await.unwrap();
9051
9052 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9053 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9054 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9055 assert!(cx.read(|cx| editor.is_dirty(cx)));
9056
9057 let save = cx.update(|cx| editor.save(project.clone(), cx));
9058 fake_server
9059 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9060 assert_eq!(
9061 params.text_document.uri,
9062 lsp::Url::from_file_path("/file.rs").unwrap()
9063 );
9064 assert_eq!(params.options.tab_size, 4);
9065 Ok(Some(vec![lsp::TextEdit::new(
9066 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
9067 ", ".to_string(),
9068 )]))
9069 })
9070 .next()
9071 .await;
9072 cx.foreground().start_waiting();
9073 save.await.unwrap();
9074 assert_eq!(
9075 editor.read_with(cx, |editor, cx| editor.text(cx)),
9076 "one, two\nthree\n"
9077 );
9078 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9079
9080 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9081 assert!(cx.read(|cx| editor.is_dirty(cx)));
9082
9083 // Ensure we can still save even if formatting hangs.
9084 fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9085 assert_eq!(
9086 params.text_document.uri,
9087 lsp::Url::from_file_path("/file.rs").unwrap()
9088 );
9089 futures::future::pending::<()>().await;
9090 unreachable!()
9091 });
9092 let save = cx.update(|cx| editor.save(project.clone(), cx));
9093 cx.foreground().advance_clock(items::FORMAT_TIMEOUT);
9094 cx.foreground().start_waiting();
9095 save.await.unwrap();
9096 assert_eq!(
9097 editor.read_with(cx, |editor, cx| editor.text(cx)),
9098 "one\ntwo\nthree\n"
9099 );
9100 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9101
9102 // Set rust language override and assert overriden tabsize is sent to language server
9103 cx.update(|cx| {
9104 cx.update_global::<Settings, _, _>(|settings, _| {
9105 settings.language_overrides.insert(
9106 "Rust".into(),
9107 LanguageOverride {
9108 tab_size: Some(8),
9109 ..Default::default()
9110 },
9111 );
9112 })
9113 });
9114
9115 let save = cx.update(|cx| editor.save(project.clone(), cx));
9116 fake_server
9117 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9118 assert_eq!(
9119 params.text_document.uri,
9120 lsp::Url::from_file_path("/file.rs").unwrap()
9121 );
9122 assert_eq!(params.options.tab_size, 8);
9123 Ok(Some(vec![]))
9124 })
9125 .next()
9126 .await;
9127 cx.foreground().start_waiting();
9128 save.await.unwrap();
9129 }
9130
9131 #[gpui::test]
9132 async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
9133 cx.foreground().forbid_parking();
9134
9135 let mut language = Language::new(
9136 LanguageConfig {
9137 name: "Rust".into(),
9138 path_suffixes: vec!["rs".to_string()],
9139 ..Default::default()
9140 },
9141 Some(tree_sitter_rust::language()),
9142 );
9143 let mut fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
9144 capabilities: lsp::ServerCapabilities {
9145 document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
9146 ..Default::default()
9147 },
9148 ..Default::default()
9149 });
9150
9151 let fs = FakeFs::new(cx.background().clone());
9152 fs.insert_file("/file.rs", Default::default()).await;
9153
9154 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
9155 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
9156 let buffer = project
9157 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
9158 .await
9159 .unwrap();
9160
9161 cx.foreground().start_waiting();
9162 let fake_server = fake_servers.next().await.unwrap();
9163
9164 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9165 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9166 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9167 assert!(cx.read(|cx| editor.is_dirty(cx)));
9168
9169 let save = cx.update(|cx| editor.save(project.clone(), cx));
9170 fake_server
9171 .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
9172 assert_eq!(
9173 params.text_document.uri,
9174 lsp::Url::from_file_path("/file.rs").unwrap()
9175 );
9176 assert_eq!(params.options.tab_size, 4);
9177 Ok(Some(vec![lsp::TextEdit::new(
9178 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
9179 ", ".to_string(),
9180 )]))
9181 })
9182 .next()
9183 .await;
9184 cx.foreground().start_waiting();
9185 save.await.unwrap();
9186 assert_eq!(
9187 editor.read_with(cx, |editor, cx| editor.text(cx)),
9188 "one, two\nthree\n"
9189 );
9190 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9191
9192 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9193 assert!(cx.read(|cx| editor.is_dirty(cx)));
9194
9195 // Ensure we can still save even if formatting hangs.
9196 fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
9197 move |params, _| async move {
9198 assert_eq!(
9199 params.text_document.uri,
9200 lsp::Url::from_file_path("/file.rs").unwrap()
9201 );
9202 futures::future::pending::<()>().await;
9203 unreachable!()
9204 },
9205 );
9206 let save = cx.update(|cx| editor.save(project.clone(), cx));
9207 cx.foreground().advance_clock(items::FORMAT_TIMEOUT);
9208 cx.foreground().start_waiting();
9209 save.await.unwrap();
9210 assert_eq!(
9211 editor.read_with(cx, |editor, cx| editor.text(cx)),
9212 "one\ntwo\nthree\n"
9213 );
9214 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9215
9216 // Set rust language override and assert overriden tabsize is sent to language server
9217 cx.update(|cx| {
9218 cx.update_global::<Settings, _, _>(|settings, _| {
9219 settings.language_overrides.insert(
9220 "Rust".into(),
9221 LanguageOverride {
9222 tab_size: Some(8),
9223 ..Default::default()
9224 },
9225 );
9226 })
9227 });
9228
9229 let save = cx.update(|cx| editor.save(project.clone(), cx));
9230 fake_server
9231 .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
9232 assert_eq!(
9233 params.text_document.uri,
9234 lsp::Url::from_file_path("/file.rs").unwrap()
9235 );
9236 assert_eq!(params.options.tab_size, 8);
9237 Ok(Some(vec![]))
9238 })
9239 .next()
9240 .await;
9241 cx.foreground().start_waiting();
9242 save.await.unwrap();
9243 }
9244
9245 #[gpui::test]
9246 async fn test_completion(cx: &mut gpui::TestAppContext) {
9247 let mut language = Language::new(
9248 LanguageConfig {
9249 name: "Rust".into(),
9250 path_suffixes: vec!["rs".to_string()],
9251 ..Default::default()
9252 },
9253 Some(tree_sitter_rust::language()),
9254 );
9255 let mut fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
9256 capabilities: lsp::ServerCapabilities {
9257 completion_provider: Some(lsp::CompletionOptions {
9258 trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
9259 ..Default::default()
9260 }),
9261 ..Default::default()
9262 },
9263 ..Default::default()
9264 });
9265
9266 let text = "
9267 one
9268 two
9269 three
9270 "
9271 .unindent();
9272
9273 let fs = FakeFs::new(cx.background().clone());
9274 fs.insert_file("/file.rs", text).await;
9275
9276 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
9277 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
9278 let buffer = project
9279 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
9280 .await
9281 .unwrap();
9282 let mut fake_server = fake_servers.next().await.unwrap();
9283
9284 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9285 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9286
9287 editor.update(cx, |editor, cx| {
9288 editor.project = Some(project);
9289 editor.change_selections(None, cx, |s| {
9290 s.select_ranges([Point::new(0, 3)..Point::new(0, 3)])
9291 });
9292 editor.handle_input(&Input(".".to_string()), cx);
9293 });
9294
9295 handle_completion_request(
9296 &mut fake_server,
9297 "/file.rs",
9298 Point::new(0, 4),
9299 vec![
9300 (Point::new(0, 4)..Point::new(0, 4), "first_completion"),
9301 (Point::new(0, 4)..Point::new(0, 4), "second_completion"),
9302 ],
9303 )
9304 .await;
9305 editor
9306 .condition(&cx, |editor, _| editor.context_menu_visible())
9307 .await;
9308
9309 let apply_additional_edits = editor.update(cx, |editor, cx| {
9310 editor.move_down(&MoveDown, cx);
9311 let apply_additional_edits = editor
9312 .confirm_completion(&ConfirmCompletion::default(), cx)
9313 .unwrap();
9314 assert_eq!(
9315 editor.text(cx),
9316 "
9317 one.second_completion
9318 two
9319 three
9320 "
9321 .unindent()
9322 );
9323 apply_additional_edits
9324 });
9325
9326 handle_resolve_completion_request(
9327 &mut fake_server,
9328 Some((Point::new(2, 5)..Point::new(2, 5), "\nadditional edit")),
9329 )
9330 .await;
9331 apply_additional_edits.await.unwrap();
9332 assert_eq!(
9333 editor.read_with(cx, |editor, cx| editor.text(cx)),
9334 "
9335 one.second_completion
9336 two
9337 three
9338 additional edit
9339 "
9340 .unindent()
9341 );
9342
9343 editor.update(cx, |editor, cx| {
9344 editor.change_selections(None, cx, |s| {
9345 s.select_ranges([
9346 Point::new(1, 3)..Point::new(1, 3),
9347 Point::new(2, 5)..Point::new(2, 5),
9348 ])
9349 });
9350
9351 editor.handle_input(&Input(" ".to_string()), cx);
9352 assert!(editor.context_menu.is_none());
9353 editor.handle_input(&Input("s".to_string()), cx);
9354 assert!(editor.context_menu.is_none());
9355 });
9356
9357 handle_completion_request(
9358 &mut fake_server,
9359 "/file.rs",
9360 Point::new(2, 7),
9361 vec![
9362 (Point::new(2, 6)..Point::new(2, 7), "fourth_completion"),
9363 (Point::new(2, 6)..Point::new(2, 7), "fifth_completion"),
9364 (Point::new(2, 6)..Point::new(2, 7), "sixth_completion"),
9365 ],
9366 )
9367 .await;
9368 editor
9369 .condition(&cx, |editor, _| editor.context_menu_visible())
9370 .await;
9371
9372 editor.update(cx, |editor, cx| {
9373 editor.handle_input(&Input("i".to_string()), cx);
9374 });
9375
9376 handle_completion_request(
9377 &mut fake_server,
9378 "/file.rs",
9379 Point::new(2, 8),
9380 vec![
9381 (Point::new(2, 6)..Point::new(2, 8), "fourth_completion"),
9382 (Point::new(2, 6)..Point::new(2, 8), "fifth_completion"),
9383 (Point::new(2, 6)..Point::new(2, 8), "sixth_completion"),
9384 ],
9385 )
9386 .await;
9387 editor
9388 .condition(&cx, |editor, _| editor.context_menu_visible())
9389 .await;
9390
9391 let apply_additional_edits = editor.update(cx, |editor, cx| {
9392 let apply_additional_edits = editor
9393 .confirm_completion(&ConfirmCompletion::default(), cx)
9394 .unwrap();
9395 assert_eq!(
9396 editor.text(cx),
9397 "
9398 one.second_completion
9399 two sixth_completion
9400 three sixth_completion
9401 additional edit
9402 "
9403 .unindent()
9404 );
9405 apply_additional_edits
9406 });
9407 handle_resolve_completion_request(&mut fake_server, None).await;
9408 apply_additional_edits.await.unwrap();
9409
9410 async fn handle_completion_request(
9411 fake: &mut FakeLanguageServer,
9412 path: &'static str,
9413 position: Point,
9414 completions: Vec<(Range<Point>, &'static str)>,
9415 ) {
9416 fake.handle_request::<lsp::request::Completion, _, _>(move |params, _| {
9417 let completions = completions.clone();
9418 async move {
9419 assert_eq!(
9420 params.text_document_position.text_document.uri,
9421 lsp::Url::from_file_path(path).unwrap()
9422 );
9423 assert_eq!(
9424 params.text_document_position.position,
9425 lsp::Position::new(position.row, position.column)
9426 );
9427 Ok(Some(lsp::CompletionResponse::Array(
9428 completions
9429 .iter()
9430 .map(|(range, new_text)| lsp::CompletionItem {
9431 label: new_text.to_string(),
9432 text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
9433 range: lsp::Range::new(
9434 lsp::Position::new(range.start.row, range.start.column),
9435 lsp::Position::new(range.start.row, range.start.column),
9436 ),
9437 new_text: new_text.to_string(),
9438 })),
9439 ..Default::default()
9440 })
9441 .collect(),
9442 )))
9443 }
9444 })
9445 .next()
9446 .await;
9447 }
9448
9449 async fn handle_resolve_completion_request(
9450 fake: &mut FakeLanguageServer,
9451 edit: Option<(Range<Point>, &'static str)>,
9452 ) {
9453 fake.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _| {
9454 let edit = edit.clone();
9455 async move {
9456 Ok(lsp::CompletionItem {
9457 additional_text_edits: edit.map(|(range, new_text)| {
9458 vec![lsp::TextEdit::new(
9459 lsp::Range::new(
9460 lsp::Position::new(range.start.row, range.start.column),
9461 lsp::Position::new(range.end.row, range.end.column),
9462 ),
9463 new_text.to_string(),
9464 )]
9465 }),
9466 ..Default::default()
9467 })
9468 }
9469 })
9470 .next()
9471 .await;
9472 }
9473 }
9474
9475 #[gpui::test]
9476 async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
9477 cx.update(|cx| cx.set_global(Settings::test(cx)));
9478 let language = Arc::new(Language::new(
9479 LanguageConfig {
9480 line_comment: Some("// ".to_string()),
9481 ..Default::default()
9482 },
9483 Some(tree_sitter_rust::language()),
9484 ));
9485
9486 let text = "
9487 fn a() {
9488 //b();
9489 // c();
9490 // d();
9491 }
9492 "
9493 .unindent();
9494
9495 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9496 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9497 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9498
9499 view.update(cx, |editor, cx| {
9500 // If multiple selections intersect a line, the line is only
9501 // toggled once.
9502 editor.change_selections(None, cx, |s| {
9503 s.select_display_ranges([
9504 DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3),
9505 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6),
9506 ])
9507 });
9508 editor.toggle_comments(&ToggleComments, cx);
9509 assert_eq!(
9510 editor.text(cx),
9511 "
9512 fn a() {
9513 b();
9514 c();
9515 d();
9516 }
9517 "
9518 .unindent()
9519 );
9520
9521 // The comment prefix is inserted at the same column for every line
9522 // in a selection.
9523 editor.change_selections(None, cx, |s| {
9524 s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)])
9525 });
9526 editor.toggle_comments(&ToggleComments, cx);
9527 assert_eq!(
9528 editor.text(cx),
9529 "
9530 fn a() {
9531 // b();
9532 // c();
9533 // d();
9534 }
9535 "
9536 .unindent()
9537 );
9538
9539 // If a selection ends at the beginning of a line, that line is not toggled.
9540 editor.change_selections(None, cx, |s| {
9541 s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)])
9542 });
9543 editor.toggle_comments(&ToggleComments, cx);
9544 assert_eq!(
9545 editor.text(cx),
9546 "
9547 fn a() {
9548 // b();
9549 c();
9550 // d();
9551 }
9552 "
9553 .unindent()
9554 );
9555 });
9556 }
9557
9558 #[gpui::test]
9559 fn test_editing_disjoint_excerpts(cx: &mut gpui::MutableAppContext) {
9560 cx.set_global(Settings::test(cx));
9561 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
9562 let multibuffer = cx.add_model(|cx| {
9563 let mut multibuffer = MultiBuffer::new(0);
9564 multibuffer.push_excerpts(
9565 buffer.clone(),
9566 [
9567 ExcerptRange {
9568 context: Point::new(0, 0)..Point::new(0, 4),
9569 primary: None,
9570 },
9571 ExcerptRange {
9572 context: Point::new(1, 0)..Point::new(1, 4),
9573 primary: None,
9574 },
9575 ],
9576 cx,
9577 );
9578 multibuffer
9579 });
9580
9581 assert_eq!(multibuffer.read(cx).read(cx).text(), "aaaa\nbbbb");
9582
9583 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
9584 view.update(cx, |view, cx| {
9585 assert_eq!(view.text(cx), "aaaa\nbbbb");
9586 view.change_selections(None, cx, |s| {
9587 s.select_ranges([
9588 Point::new(0, 0)..Point::new(0, 0),
9589 Point::new(1, 0)..Point::new(1, 0),
9590 ])
9591 });
9592
9593 view.handle_input(&Input("X".to_string()), cx);
9594 assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
9595 assert_eq!(
9596 view.selections.ranges(cx),
9597 [
9598 Point::new(0, 1)..Point::new(0, 1),
9599 Point::new(1, 1)..Point::new(1, 1),
9600 ]
9601 )
9602 });
9603 }
9604
9605 #[gpui::test]
9606 fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) {
9607 cx.set_global(Settings::test(cx));
9608 let (initial_text, excerpt_ranges) = marked_text_ranges(indoc! {"
9609 [aaaa
9610 (bbbb]
9611 cccc)"});
9612 let excerpt_ranges = excerpt_ranges.into_iter().map(|context| ExcerptRange {
9613 context,
9614 primary: None,
9615 });
9616 let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx));
9617 let multibuffer = cx.add_model(|cx| {
9618 let mut multibuffer = MultiBuffer::new(0);
9619 multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
9620 multibuffer
9621 });
9622
9623 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
9624 view.update(cx, |view, cx| {
9625 let (expected_text, selection_ranges) = marked_text_ranges(indoc! {"
9626 aaaa
9627 b|bbb
9628 b|bb|b
9629 cccc"});
9630 assert_eq!(view.text(cx), expected_text);
9631 view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
9632
9633 view.handle_input(&Input("X".to_string()), cx);
9634
9635 let (expected_text, expected_selections) = marked_text_ranges(indoc! {"
9636 aaaa
9637 bX|bbXb
9638 bX|bbX|b
9639 cccc"});
9640 assert_eq!(view.text(cx), expected_text);
9641 assert_eq!(view.selections.ranges(cx), expected_selections);
9642
9643 view.newline(&Newline, cx);
9644 let (expected_text, expected_selections) = marked_text_ranges(indoc! {"
9645 aaaa
9646 bX
9647 |bbX
9648 b
9649 bX
9650 |bbX
9651 |b
9652 cccc"});
9653 assert_eq!(view.text(cx), expected_text);
9654 assert_eq!(view.selections.ranges(cx), expected_selections);
9655 });
9656 }
9657
9658 #[gpui::test]
9659 fn test_refresh_selections(cx: &mut gpui::MutableAppContext) {
9660 cx.set_global(Settings::test(cx));
9661 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
9662 let mut excerpt1_id = None;
9663 let multibuffer = cx.add_model(|cx| {
9664 let mut multibuffer = MultiBuffer::new(0);
9665 excerpt1_id = multibuffer
9666 .push_excerpts(
9667 buffer.clone(),
9668 [
9669 ExcerptRange {
9670 context: Point::new(0, 0)..Point::new(1, 4),
9671 primary: None,
9672 },
9673 ExcerptRange {
9674 context: Point::new(1, 0)..Point::new(2, 4),
9675 primary: None,
9676 },
9677 ],
9678 cx,
9679 )
9680 .into_iter()
9681 .next();
9682 multibuffer
9683 });
9684 assert_eq!(
9685 multibuffer.read(cx).read(cx).text(),
9686 "aaaa\nbbbb\nbbbb\ncccc"
9687 );
9688 let (_, editor) = cx.add_window(Default::default(), |cx| {
9689 let mut editor = build_editor(multibuffer.clone(), cx);
9690 let snapshot = editor.snapshot(cx);
9691 editor.change_selections(None, cx, |s| {
9692 s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
9693 });
9694 editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
9695 assert_eq!(
9696 editor.selections.ranges(cx),
9697 [
9698 Point::new(1, 3)..Point::new(1, 3),
9699 Point::new(2, 1)..Point::new(2, 1),
9700 ]
9701 );
9702 editor
9703 });
9704
9705 // Refreshing selections is a no-op when excerpts haven't changed.
9706 editor.update(cx, |editor, cx| {
9707 editor.change_selections(None, cx, |s| {
9708 s.refresh();
9709 });
9710 assert_eq!(
9711 editor.selections.ranges(cx),
9712 [
9713 Point::new(1, 3)..Point::new(1, 3),
9714 Point::new(2, 1)..Point::new(2, 1),
9715 ]
9716 );
9717 });
9718
9719 multibuffer.update(cx, |multibuffer, cx| {
9720 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
9721 });
9722 editor.update(cx, |editor, cx| {
9723 // Removing an excerpt causes the first selection to become degenerate.
9724 assert_eq!(
9725 editor.selections.ranges(cx),
9726 [
9727 Point::new(0, 0)..Point::new(0, 0),
9728 Point::new(0, 1)..Point::new(0, 1)
9729 ]
9730 );
9731
9732 // Refreshing selections will relocate the first selection to the original buffer
9733 // location.
9734 editor.change_selections(None, cx, |s| {
9735 s.refresh();
9736 });
9737 assert_eq!(
9738 editor.selections.ranges(cx),
9739 [
9740 Point::new(0, 1)..Point::new(0, 1),
9741 Point::new(0, 3)..Point::new(0, 3)
9742 ]
9743 );
9744 assert!(editor.selections.pending_anchor().is_some());
9745 });
9746 }
9747
9748 #[gpui::test]
9749 fn test_refresh_selections_while_selecting_with_mouse(cx: &mut gpui::MutableAppContext) {
9750 cx.set_global(Settings::test(cx));
9751 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
9752 let mut excerpt1_id = None;
9753 let multibuffer = cx.add_model(|cx| {
9754 let mut multibuffer = MultiBuffer::new(0);
9755 excerpt1_id = multibuffer
9756 .push_excerpts(
9757 buffer.clone(),
9758 [
9759 ExcerptRange {
9760 context: Point::new(0, 0)..Point::new(1, 4),
9761 primary: None,
9762 },
9763 ExcerptRange {
9764 context: Point::new(1, 0)..Point::new(2, 4),
9765 primary: None,
9766 },
9767 ],
9768 cx,
9769 )
9770 .into_iter()
9771 .next();
9772 multibuffer
9773 });
9774 assert_eq!(
9775 multibuffer.read(cx).read(cx).text(),
9776 "aaaa\nbbbb\nbbbb\ncccc"
9777 );
9778 let (_, editor) = cx.add_window(Default::default(), |cx| {
9779 let mut editor = build_editor(multibuffer.clone(), cx);
9780 let snapshot = editor.snapshot(cx);
9781 editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
9782 assert_eq!(
9783 editor.selections.ranges(cx),
9784 [Point::new(1, 3)..Point::new(1, 3)]
9785 );
9786 editor
9787 });
9788
9789 multibuffer.update(cx, |multibuffer, cx| {
9790 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
9791 });
9792 editor.update(cx, |editor, cx| {
9793 assert_eq!(
9794 editor.selections.ranges(cx),
9795 [Point::new(0, 0)..Point::new(0, 0)]
9796 );
9797
9798 // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
9799 editor.change_selections(None, cx, |s| {
9800 s.refresh();
9801 });
9802 assert_eq!(
9803 editor.selections.ranges(cx),
9804 [Point::new(0, 3)..Point::new(0, 3)]
9805 );
9806 assert!(editor.selections.pending_anchor().is_some());
9807 });
9808 }
9809
9810 #[gpui::test]
9811 async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
9812 cx.update(|cx| cx.set_global(Settings::test(cx)));
9813 let language = Arc::new(Language::new(
9814 LanguageConfig {
9815 brackets: vec![
9816 BracketPair {
9817 start: "{".to_string(),
9818 end: "}".to_string(),
9819 close: true,
9820 newline: true,
9821 },
9822 BracketPair {
9823 start: "/* ".to_string(),
9824 end: " */".to_string(),
9825 close: true,
9826 newline: true,
9827 },
9828 ],
9829 ..Default::default()
9830 },
9831 Some(tree_sitter_rust::language()),
9832 ));
9833
9834 let text = concat!(
9835 "{ }\n", // Suppress rustfmt
9836 " x\n", //
9837 " /* */\n", //
9838 "x\n", //
9839 "{{} }\n", //
9840 );
9841
9842 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9843 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9844 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9845 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9846 .await;
9847
9848 view.update(cx, |view, cx| {
9849 view.change_selections(None, cx, |s| {
9850 s.select_display_ranges([
9851 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
9852 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
9853 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
9854 ])
9855 });
9856 view.newline(&Newline, cx);
9857
9858 assert_eq!(
9859 view.buffer().read(cx).read(cx).text(),
9860 concat!(
9861 "{ \n", // Suppress rustfmt
9862 "\n", //
9863 "}\n", //
9864 " x\n", //
9865 " /* \n", //
9866 " \n", //
9867 " */\n", //
9868 "x\n", //
9869 "{{} \n", //
9870 "}\n", //
9871 )
9872 );
9873 });
9874 }
9875
9876 #[gpui::test]
9877 fn test_highlighted_ranges(cx: &mut gpui::MutableAppContext) {
9878 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
9879
9880 cx.set_global(Settings::test(cx));
9881 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
9882
9883 editor.update(cx, |editor, cx| {
9884 struct Type1;
9885 struct Type2;
9886
9887 let buffer = buffer.read(cx).snapshot(cx);
9888
9889 let anchor_range = |range: Range<Point>| {
9890 buffer.anchor_after(range.start)..buffer.anchor_after(range.end)
9891 };
9892
9893 editor.highlight_background::<Type1>(
9894 vec![
9895 anchor_range(Point::new(2, 1)..Point::new(2, 3)),
9896 anchor_range(Point::new(4, 2)..Point::new(4, 4)),
9897 anchor_range(Point::new(6, 3)..Point::new(6, 5)),
9898 anchor_range(Point::new(8, 4)..Point::new(8, 6)),
9899 ],
9900 |_| Color::red(),
9901 cx,
9902 );
9903 editor.highlight_background::<Type2>(
9904 vec![
9905 anchor_range(Point::new(3, 2)..Point::new(3, 5)),
9906 anchor_range(Point::new(5, 3)..Point::new(5, 6)),
9907 anchor_range(Point::new(7, 4)..Point::new(7, 7)),
9908 anchor_range(Point::new(9, 5)..Point::new(9, 8)),
9909 ],
9910 |_| Color::green(),
9911 cx,
9912 );
9913
9914 let snapshot = editor.snapshot(cx);
9915 let mut highlighted_ranges = editor.background_highlights_in_range(
9916 anchor_range(Point::new(3, 4)..Point::new(7, 4)),
9917 &snapshot,
9918 cx.global::<Settings>().theme.as_ref(),
9919 );
9920 // Enforce a consistent ordering based on color without relying on the ordering of the
9921 // highlight's `TypeId` which is non-deterministic.
9922 highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
9923 assert_eq!(
9924 highlighted_ranges,
9925 &[
9926 (
9927 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
9928 Color::green(),
9929 ),
9930 (
9931 DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
9932 Color::green(),
9933 ),
9934 (
9935 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
9936 Color::red(),
9937 ),
9938 (
9939 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
9940 Color::red(),
9941 ),
9942 ]
9943 );
9944 assert_eq!(
9945 editor.background_highlights_in_range(
9946 anchor_range(Point::new(5, 6)..Point::new(6, 4)),
9947 &snapshot,
9948 cx.global::<Settings>().theme.as_ref(),
9949 ),
9950 &[(
9951 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
9952 Color::red(),
9953 )]
9954 );
9955 });
9956 }
9957
9958 #[gpui::test]
9959 fn test_following(cx: &mut gpui::MutableAppContext) {
9960 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
9961
9962 cx.set_global(Settings::test(cx));
9963
9964 let (_, leader) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
9965 let (_, follower) = cx.add_window(
9966 WindowOptions {
9967 bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
9968 ..Default::default()
9969 },
9970 |cx| build_editor(buffer.clone(), cx),
9971 );
9972
9973 let pending_update = Rc::new(RefCell::new(None));
9974 follower.update(cx, {
9975 let update = pending_update.clone();
9976 |_, cx| {
9977 cx.subscribe(&leader, move |_, leader, event, cx| {
9978 leader
9979 .read(cx)
9980 .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
9981 })
9982 .detach();
9983 }
9984 });
9985
9986 // Update the selections only
9987 leader.update(cx, |leader, cx| {
9988 leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
9989 });
9990 follower.update(cx, |follower, cx| {
9991 follower
9992 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
9993 .unwrap();
9994 });
9995 assert_eq!(follower.read(cx).selections.ranges(cx), vec![1..1]);
9996
9997 // Update the scroll position only
9998 leader.update(cx, |leader, cx| {
9999 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
10000 });
10001 follower.update(cx, |follower, cx| {
10002 follower
10003 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10004 .unwrap();
10005 });
10006 assert_eq!(
10007 follower.update(cx, |follower, cx| follower.scroll_position(cx)),
10008 vec2f(1.5, 3.5)
10009 );
10010
10011 // Update the selections and scroll position
10012 leader.update(cx, |leader, cx| {
10013 leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
10014 leader.request_autoscroll(Autoscroll::Newest, cx);
10015 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
10016 });
10017 follower.update(cx, |follower, cx| {
10018 let initial_scroll_position = follower.scroll_position(cx);
10019 follower
10020 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10021 .unwrap();
10022 assert_eq!(follower.scroll_position(cx), initial_scroll_position);
10023 assert!(follower.autoscroll_request.is_some());
10024 });
10025 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0]);
10026
10027 // Creating a pending selection that precedes another selection
10028 leader.update(cx, |leader, cx| {
10029 leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
10030 leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
10031 });
10032 follower.update(cx, |follower, cx| {
10033 follower
10034 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10035 .unwrap();
10036 });
10037 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0, 1..1]);
10038
10039 // Extend the pending selection so that it surrounds another selection
10040 leader.update(cx, |leader, cx| {
10041 leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
10042 });
10043 follower.update(cx, |follower, cx| {
10044 follower
10045 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10046 .unwrap();
10047 });
10048 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..2]);
10049 }
10050
10051 #[test]
10052 fn test_combine_syntax_and_fuzzy_match_highlights() {
10053 let string = "abcdefghijklmnop";
10054 let syntax_ranges = [
10055 (
10056 0..3,
10057 HighlightStyle {
10058 color: Some(Color::red()),
10059 ..Default::default()
10060 },
10061 ),
10062 (
10063 4..8,
10064 HighlightStyle {
10065 color: Some(Color::green()),
10066 ..Default::default()
10067 },
10068 ),
10069 ];
10070 let match_indices = [4, 6, 7, 8];
10071 assert_eq!(
10072 combine_syntax_and_fuzzy_match_highlights(
10073 &string,
10074 Default::default(),
10075 syntax_ranges.into_iter(),
10076 &match_indices,
10077 ),
10078 &[
10079 (
10080 0..3,
10081 HighlightStyle {
10082 color: Some(Color::red()),
10083 ..Default::default()
10084 },
10085 ),
10086 (
10087 4..5,
10088 HighlightStyle {
10089 color: Some(Color::green()),
10090 weight: Some(fonts::Weight::BOLD),
10091 ..Default::default()
10092 },
10093 ),
10094 (
10095 5..6,
10096 HighlightStyle {
10097 color: Some(Color::green()),
10098 ..Default::default()
10099 },
10100 ),
10101 (
10102 6..8,
10103 HighlightStyle {
10104 color: Some(Color::green()),
10105 weight: Some(fonts::Weight::BOLD),
10106 ..Default::default()
10107 },
10108 ),
10109 (
10110 8..9,
10111 HighlightStyle {
10112 weight: Some(fonts::Weight::BOLD),
10113 ..Default::default()
10114 },
10115 ),
10116 ]
10117 );
10118 }
10119
10120 fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
10121 let point = DisplayPoint::new(row as u32, column as u32);
10122 point..point
10123 }
10124
10125 fn assert_selection_ranges(
10126 marked_text: &str,
10127 selection_marker_pairs: Vec<(char, char)>,
10128 view: &mut Editor,
10129 cx: &mut ViewContext<Editor>,
10130 ) {
10131 let snapshot = view.snapshot(cx).display_snapshot;
10132 let mut marker_chars = Vec::new();
10133 for (start, end) in selection_marker_pairs.iter() {
10134 marker_chars.push(*start);
10135 marker_chars.push(*end);
10136 }
10137 let (_, markers) = marked_text_by(marked_text, marker_chars);
10138 let asserted_ranges: Vec<Range<DisplayPoint>> = selection_marker_pairs
10139 .iter()
10140 .map(|(start, end)| {
10141 let start = markers.get(start).unwrap()[0].to_display_point(&snapshot);
10142 let end = markers.get(end).unwrap()[0].to_display_point(&snapshot);
10143 start..end
10144 })
10145 .collect();
10146 assert_eq!(
10147 view.selections.display_ranges(cx),
10148 &asserted_ranges[..],
10149 "Assert selections are {}",
10150 marked_text
10151 );
10152 }
10153}
10154
10155trait RangeExt<T> {
10156 fn sorted(&self) -> Range<T>;
10157 fn to_inclusive(&self) -> RangeInclusive<T>;
10158}
10159
10160impl<T: Ord + Clone> RangeExt<T> for Range<T> {
10161 fn sorted(&self) -> Self {
10162 cmp::min(&self.start, &self.end).clone()..cmp::max(&self.start, &self.end).clone()
10163 }
10164
10165 fn to_inclusive(&self) -> RangeInclusive<T> {
10166 self.start.clone()..=self.end.clone()
10167 }
10168}