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