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