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