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