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