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