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