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