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 snapshot = self.snapshot(cx);
2443 let (buffer, buffer_position) = if let Some(output) = self
2444 .buffer
2445 .read(cx)
2446 .text_anchor_for_position(action.0.to_point(&snapshot.display_snapshot), cx)
2447 {
2448 output
2449 } else {
2450 return;
2451 };
2452
2453 let hover = project.update(cx, |project, cx| {
2454 project.hover(&buffer, buffer_position.clone(), cx)
2455 });
2456
2457 let point = action.0.clone();
2458
2459 let id = post_inc(&mut self.next_completion_id);
2460 let task = cx.spawn_weak(|this, mut cx| {
2461 async move {
2462 // TODO: what to show while language server is loading?
2463 let text: String = match hover.await? {
2464 None => "Language server is warming up...".into(),
2465 Some(hover) => match hover.contents {
2466 lsp::HoverContents::Scalar(marked_string) => match marked_string {
2467 lsp::MarkedString::String(string) => string,
2468 lsp::MarkedString::LanguageString(string) => string.value,
2469 },
2470 lsp::HoverContents::Array(marked_strings) => {
2471 // TODO: what to do?
2472 todo!()
2473 }
2474 lsp::HoverContents::Markup(markup) => markup.value,
2475 },
2476 };
2477
2478 let mut hover_popover = HoverPopover {
2479 // TODO: fix tooltip to beginning of symbol based on range
2480 point,
2481 text,
2482 runs: Vec::new(),
2483 };
2484
2485 if let Some(this) = this.upgrade(&cx) {
2486 this.update(&mut cx, |this, cx| {
2487 if !matches!(
2488 this.context_menu.as_ref(),
2489 None | Some(ContextMenu::Hover(_))
2490 ) {
2491 return;
2492 }
2493
2494 if this.focused {
2495 this.show_context_menu(ContextMenu::Hover(hover_popover), cx);
2496 }
2497
2498 cx.notify();
2499 });
2500 }
2501 Ok::<_, anyhow::Error>(())
2502 }
2503 .log_err()
2504 });
2505 self.completion_tasks.push((id, task));
2506 }
2507
2508 async fn open_project_transaction(
2509 this: ViewHandle<Editor>,
2510 workspace: ViewHandle<Workspace>,
2511 transaction: ProjectTransaction,
2512 title: String,
2513 mut cx: AsyncAppContext,
2514 ) -> Result<()> {
2515 let replica_id = this.read_with(&cx, |this, cx| this.replica_id(cx));
2516
2517 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
2518 entries.sort_unstable_by_key(|(buffer, _)| {
2519 buffer.read_with(&cx, |buffer, _| buffer.file().map(|f| f.path().clone()))
2520 });
2521
2522 // If the project transaction's edits are all contained within this editor, then
2523 // avoid opening a new editor to display them.
2524
2525 if let Some((buffer, transaction)) = entries.first() {
2526 if entries.len() == 1 {
2527 let excerpt = this.read_with(&cx, |editor, cx| {
2528 editor
2529 .buffer()
2530 .read(cx)
2531 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
2532 });
2533 if let Some((excerpted_buffer, excerpt_range)) = excerpt {
2534 if excerpted_buffer == *buffer {
2535 let snapshot = buffer.read_with(&cx, |buffer, _| buffer.snapshot());
2536 let excerpt_range = excerpt_range.to_offset(&snapshot);
2537 if snapshot
2538 .edited_ranges_for_transaction(transaction)
2539 .all(|range| {
2540 excerpt_range.start <= range.start && excerpt_range.end >= range.end
2541 })
2542 {
2543 return Ok(());
2544 }
2545 }
2546 }
2547 }
2548 } else {
2549 return Ok(());
2550 }
2551
2552 let mut ranges_to_highlight = Vec::new();
2553 let excerpt_buffer = cx.add_model(|cx| {
2554 let mut multibuffer = MultiBuffer::new(replica_id).with_title(title);
2555 for (buffer, transaction) in &entries {
2556 let snapshot = buffer.read(cx).snapshot();
2557 ranges_to_highlight.extend(
2558 multibuffer.push_excerpts_with_context_lines(
2559 buffer.clone(),
2560 snapshot
2561 .edited_ranges_for_transaction::<usize>(transaction)
2562 .collect(),
2563 1,
2564 cx,
2565 ),
2566 );
2567 }
2568 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)));
2569 multibuffer
2570 });
2571
2572 workspace.update(&mut cx, |workspace, cx| {
2573 let project = workspace.project().clone();
2574 let editor =
2575 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
2576 workspace.add_item(Box::new(editor.clone()), cx);
2577 editor.update(cx, |editor, cx| {
2578 editor.highlight_background::<Self>(
2579 ranges_to_highlight,
2580 |theme| theme.editor.highlighted_line_background,
2581 cx,
2582 );
2583 });
2584 });
2585
2586 Ok(())
2587 }
2588
2589 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
2590 let project = self.project.as_ref()?;
2591 let buffer = self.buffer.read(cx);
2592 let newest_selection = self.selections.newest_anchor().clone();
2593 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
2594 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
2595 if start_buffer != end_buffer {
2596 return None;
2597 }
2598
2599 let actions = project.update(cx, |project, cx| {
2600 project.code_actions(&start_buffer, start..end, cx)
2601 });
2602 self.code_actions_task = Some(cx.spawn_weak(|this, mut cx| async move {
2603 let actions = actions.await;
2604 if let Some(this) = this.upgrade(&cx) {
2605 this.update(&mut cx, |this, cx| {
2606 this.available_code_actions = actions.log_err().and_then(|actions| {
2607 if actions.is_empty() {
2608 None
2609 } else {
2610 Some((start_buffer, actions.into()))
2611 }
2612 });
2613 cx.notify();
2614 })
2615 }
2616 }));
2617 None
2618 }
2619
2620 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
2621 if self.pending_rename.is_some() {
2622 return None;
2623 }
2624
2625 let project = self.project.as_ref()?;
2626 let buffer = self.buffer.read(cx);
2627 let newest_selection = self.selections.newest_anchor().clone();
2628 let cursor_position = newest_selection.head();
2629 let (cursor_buffer, cursor_buffer_position) =
2630 buffer.text_anchor_for_position(cursor_position.clone(), cx)?;
2631 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
2632 if cursor_buffer != tail_buffer {
2633 return None;
2634 }
2635
2636 let highlights = project.update(cx, |project, cx| {
2637 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
2638 });
2639
2640 self.document_highlights_task = Some(cx.spawn_weak(|this, mut cx| async move {
2641 let highlights = highlights.log_err().await;
2642 if let Some((this, highlights)) = this.upgrade(&cx).zip(highlights) {
2643 this.update(&mut cx, |this, cx| {
2644 if this.pending_rename.is_some() {
2645 return;
2646 }
2647
2648 let buffer_id = cursor_position.buffer_id;
2649 let buffer = this.buffer.read(cx);
2650 if !buffer
2651 .text_anchor_for_position(cursor_position, cx)
2652 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
2653 {
2654 return;
2655 }
2656
2657 let cursor_buffer_snapshot = cursor_buffer.read(cx);
2658 let mut write_ranges = Vec::new();
2659 let mut read_ranges = Vec::new();
2660 for highlight in highlights {
2661 for (excerpt_id, excerpt_range) in
2662 buffer.excerpts_for_buffer(&cursor_buffer, cx)
2663 {
2664 let start = highlight
2665 .range
2666 .start
2667 .max(&excerpt_range.start, cursor_buffer_snapshot);
2668 let end = highlight
2669 .range
2670 .end
2671 .min(&excerpt_range.end, cursor_buffer_snapshot);
2672 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
2673 continue;
2674 }
2675
2676 let range = Anchor {
2677 buffer_id,
2678 excerpt_id: excerpt_id.clone(),
2679 text_anchor: start,
2680 }..Anchor {
2681 buffer_id,
2682 excerpt_id,
2683 text_anchor: end,
2684 };
2685 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
2686 write_ranges.push(range);
2687 } else {
2688 read_ranges.push(range);
2689 }
2690 }
2691 }
2692
2693 this.highlight_background::<DocumentHighlightRead>(
2694 read_ranges,
2695 |theme| theme.editor.document_highlight_read_background,
2696 cx,
2697 );
2698 this.highlight_background::<DocumentHighlightWrite>(
2699 write_ranges,
2700 |theme| theme.editor.document_highlight_write_background,
2701 cx,
2702 );
2703 cx.notify();
2704 });
2705 }
2706 }));
2707 None
2708 }
2709
2710 pub fn render_code_actions_indicator(
2711 &self,
2712 style: &EditorStyle,
2713 cx: &mut RenderContext<Self>,
2714 ) -> Option<ElementBox> {
2715 if self.available_code_actions.is_some() {
2716 enum Tag {}
2717 Some(
2718 MouseEventHandler::new::<Tag, _, _>(0, cx, |_, _| {
2719 Svg::new("icons/zap.svg")
2720 .with_color(style.code_actions_indicator)
2721 .boxed()
2722 })
2723 .with_cursor_style(CursorStyle::PointingHand)
2724 .with_padding(Padding::uniform(3.))
2725 .on_mouse_down(|_, cx| {
2726 cx.dispatch_action(ToggleCodeActions {
2727 deployed_from_indicator: true,
2728 });
2729 })
2730 .boxed(),
2731 )
2732 } else {
2733 None
2734 }
2735 }
2736
2737 pub fn context_menu_visible(&self) -> bool {
2738 self.context_menu
2739 .as_ref()
2740 .map_or(false, |menu| menu.visible())
2741 }
2742
2743 pub fn render_context_menu(
2744 &self,
2745 cursor_position: DisplayPoint,
2746 style: EditorStyle,
2747 cx: &mut RenderContext<Editor>,
2748 ) -> Option<(DisplayPoint, ElementBox)> {
2749 self.context_menu
2750 .as_ref()
2751 .map(|menu| menu.render(cursor_position, style))
2752 }
2753
2754 fn show_context_menu(&mut self, menu: ContextMenu, cx: &mut ViewContext<Self>) {
2755 if !matches!(menu, ContextMenu::Completions(_)) {
2756 self.completion_tasks.clear();
2757 }
2758 self.context_menu = Some(menu);
2759 cx.notify();
2760 }
2761
2762 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
2763 cx.notify();
2764 self.completion_tasks.clear();
2765 self.context_menu.take()
2766 }
2767
2768 pub fn insert_snippet(
2769 &mut self,
2770 insertion_ranges: &[Range<usize>],
2771 snippet: Snippet,
2772 cx: &mut ViewContext<Self>,
2773 ) -> Result<()> {
2774 let tabstops = self.buffer.update(cx, |buffer, cx| {
2775 let snippet_text: Arc<str> = snippet.text.clone().into();
2776 buffer.edit_with_autoindent(
2777 insertion_ranges
2778 .iter()
2779 .cloned()
2780 .map(|range| (range, snippet_text.clone())),
2781 cx,
2782 );
2783
2784 let snapshot = &*buffer.read(cx);
2785 let snippet = &snippet;
2786 snippet
2787 .tabstops
2788 .iter()
2789 .map(|tabstop| {
2790 let mut tabstop_ranges = tabstop
2791 .iter()
2792 .flat_map(|tabstop_range| {
2793 let mut delta = 0 as isize;
2794 insertion_ranges.iter().map(move |insertion_range| {
2795 let insertion_start = insertion_range.start as isize + delta;
2796 delta +=
2797 snippet.text.len() as isize - insertion_range.len() as isize;
2798
2799 let start = snapshot.anchor_before(
2800 (insertion_start + tabstop_range.start) as usize,
2801 );
2802 let end = snapshot
2803 .anchor_after((insertion_start + tabstop_range.end) as usize);
2804 start..end
2805 })
2806 })
2807 .collect::<Vec<_>>();
2808 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
2809 tabstop_ranges
2810 })
2811 .collect::<Vec<_>>()
2812 });
2813
2814 if let Some(tabstop) = tabstops.first() {
2815 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
2816 s.select_ranges(tabstop.iter().cloned());
2817 });
2818 self.snippet_stack.push(SnippetState {
2819 active_index: 0,
2820 ranges: tabstops,
2821 });
2822 }
2823
2824 Ok(())
2825 }
2826
2827 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
2828 self.move_to_snippet_tabstop(Bias::Right, cx)
2829 }
2830
2831 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
2832 self.move_to_snippet_tabstop(Bias::Left, cx)
2833 }
2834
2835 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
2836 if let Some(mut snippet) = self.snippet_stack.pop() {
2837 match bias {
2838 Bias::Left => {
2839 if snippet.active_index > 0 {
2840 snippet.active_index -= 1;
2841 } else {
2842 self.snippet_stack.push(snippet);
2843 return false;
2844 }
2845 }
2846 Bias::Right => {
2847 if snippet.active_index + 1 < snippet.ranges.len() {
2848 snippet.active_index += 1;
2849 } else {
2850 self.snippet_stack.push(snippet);
2851 return false;
2852 }
2853 }
2854 }
2855 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
2856 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
2857 s.select_anchor_ranges(current_ranges.into_iter().cloned())
2858 });
2859 // If snippet state is not at the last tabstop, push it back on the stack
2860 if snippet.active_index + 1 < snippet.ranges.len() {
2861 self.snippet_stack.push(snippet);
2862 }
2863 return true;
2864 }
2865 }
2866
2867 false
2868 }
2869
2870 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
2871 self.transact(cx, |this, cx| {
2872 this.select_all(&SelectAll, cx);
2873 this.insert("", cx);
2874 });
2875 }
2876
2877 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
2878 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2879 let mut selections = self.selections.all::<Point>(cx);
2880 if !self.selections.line_mode {
2881 for selection in &mut selections {
2882 if selection.is_empty() {
2883 let old_head = selection.head();
2884 let mut new_head =
2885 movement::left(&display_map, old_head.to_display_point(&display_map))
2886 .to_point(&display_map);
2887 if let Some((buffer, line_buffer_range)) = display_map
2888 .buffer_snapshot
2889 .buffer_line_for_row(old_head.row)
2890 {
2891 let indent_column =
2892 buffer.indent_column_for_line(line_buffer_range.start.row);
2893 let language_name = buffer.language().map(|language| language.name());
2894 let indent = cx.global::<Settings>().tab_size(language_name.as_deref());
2895 if old_head.column <= indent_column && old_head.column > 0 {
2896 new_head = cmp::min(
2897 new_head,
2898 Point::new(old_head.row, ((old_head.column - 1) / indent) * indent),
2899 );
2900 }
2901 }
2902
2903 selection.set_head(new_head, SelectionGoal::None);
2904 }
2905 }
2906 }
2907
2908 self.transact(cx, |this, cx| {
2909 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
2910 this.insert("", cx);
2911 });
2912 }
2913
2914 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
2915 self.transact(cx, |this, cx| {
2916 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
2917 let line_mode = s.line_mode;
2918 s.move_with(|map, selection| {
2919 if selection.is_empty() && !line_mode {
2920 let cursor = movement::right(map, selection.head());
2921 selection.set_head(cursor, SelectionGoal::None);
2922 }
2923 })
2924 });
2925 this.insert(&"", cx);
2926 });
2927 }
2928
2929 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
2930 if self.move_to_prev_snippet_tabstop(cx) {
2931 return;
2932 }
2933
2934 self.outdent(&Outdent, cx);
2935 }
2936
2937 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
2938 if self.move_to_next_snippet_tabstop(cx) {
2939 return;
2940 }
2941
2942 let mut selections = self.selections.all_adjusted(cx);
2943 if selections.iter().all(|s| s.is_empty()) {
2944 self.transact(cx, |this, cx| {
2945 this.buffer.update(cx, |buffer, cx| {
2946 for selection in &mut selections {
2947 let language_name =
2948 buffer.language_at(selection.start, cx).map(|l| l.name());
2949 let tab_size = cx.global::<Settings>().tab_size(language_name.as_deref());
2950 let char_column = buffer
2951 .read(cx)
2952 .text_for_range(Point::new(selection.start.row, 0)..selection.start)
2953 .flat_map(str::chars)
2954 .count();
2955 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
2956 buffer.edit(
2957 [(
2958 selection.start..selection.start,
2959 " ".repeat(chars_to_next_tab_stop as usize),
2960 )],
2961 cx,
2962 );
2963 selection.start.column += chars_to_next_tab_stop;
2964 selection.end = selection.start;
2965 }
2966 });
2967 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
2968 s.select(selections);
2969 });
2970 });
2971 } else {
2972 self.indent(&Indent, cx);
2973 }
2974 }
2975
2976 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
2977 let mut selections = self.selections.all::<Point>(cx);
2978 self.transact(cx, |this, cx| {
2979 let mut last_indent = None;
2980 this.buffer.update(cx, |buffer, cx| {
2981 let snapshot = buffer.snapshot(cx);
2982 for selection in &mut selections {
2983 let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
2984 let tab_size = cx.global::<Settings>().tab_size(language_name.as_deref());
2985 let mut start_row = selection.start.row;
2986 let mut end_row = selection.end.row + 1;
2987
2988 // If a selection ends at the beginning of a line, don't indent
2989 // that last line.
2990 if selection.end.column == 0 {
2991 end_row -= 1;
2992 }
2993
2994 // Avoid re-indenting a row that has already been indented by a
2995 // previous selection, but still update this selection's column
2996 // to reflect that indentation.
2997 if let Some((last_indent_row, last_indent_len)) = last_indent {
2998 if last_indent_row == selection.start.row {
2999 selection.start.column += last_indent_len;
3000 start_row += 1;
3001 }
3002 if last_indent_row == selection.end.row {
3003 selection.end.column += last_indent_len;
3004 }
3005 }
3006
3007 for row in start_row..end_row {
3008 let indent_column = snapshot.indent_column_for_line(row);
3009 let columns_to_next_tab_stop = tab_size - (indent_column % tab_size);
3010 let row_start = Point::new(row, 0);
3011 buffer.edit(
3012 [(
3013 row_start..row_start,
3014 " ".repeat(columns_to_next_tab_stop as usize),
3015 )],
3016 cx,
3017 );
3018
3019 // Update this selection's endpoints to reflect the indentation.
3020 if row == selection.start.row {
3021 selection.start.column += columns_to_next_tab_stop as u32;
3022 }
3023 if row == selection.end.row {
3024 selection.end.column += columns_to_next_tab_stop as u32;
3025 }
3026
3027 last_indent = Some((row, columns_to_next_tab_stop as u32));
3028 }
3029 }
3030 });
3031
3032 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3033 s.select(selections);
3034 });
3035 });
3036 }
3037
3038 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
3039 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3040 let selections = self.selections.all::<Point>(cx);
3041 let mut deletion_ranges = Vec::new();
3042 let mut last_outdent = None;
3043 {
3044 let buffer = self.buffer.read(cx);
3045 let snapshot = buffer.snapshot(cx);
3046 for selection in &selections {
3047 let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
3048 let tab_size = cx.global::<Settings>().tab_size(language_name.as_deref());
3049 let mut rows = selection.spanned_rows(false, &display_map);
3050
3051 // Avoid re-outdenting a row that has already been outdented by a
3052 // previous selection.
3053 if let Some(last_row) = last_outdent {
3054 if last_row == rows.start {
3055 rows.start += 1;
3056 }
3057 }
3058
3059 for row in rows {
3060 let column = snapshot.indent_column_for_line(row);
3061 if column > 0 {
3062 let mut deletion_len = column % tab_size;
3063 if deletion_len == 0 {
3064 deletion_len = tab_size;
3065 }
3066 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
3067 last_outdent = Some(row);
3068 }
3069 }
3070 }
3071 }
3072
3073 self.transact(cx, |this, cx| {
3074 this.buffer.update(cx, |buffer, cx| {
3075 let empty_str: Arc<str> = "".into();
3076 buffer.edit(
3077 deletion_ranges
3078 .into_iter()
3079 .map(|range| (range, empty_str.clone())),
3080 cx,
3081 );
3082 });
3083 let selections = this.selections.all::<usize>(cx);
3084 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
3085 });
3086 }
3087
3088 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
3089 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3090 let selections = self.selections.all::<Point>(cx);
3091
3092 let mut new_cursors = Vec::new();
3093 let mut edit_ranges = Vec::new();
3094 let mut selections = selections.iter().peekable();
3095 while let Some(selection) = selections.next() {
3096 let mut rows = selection.spanned_rows(false, &display_map);
3097 let goal_display_column = selection.head().to_display_point(&display_map).column();
3098
3099 // Accumulate contiguous regions of rows that we want to delete.
3100 while let Some(next_selection) = selections.peek() {
3101 let next_rows = next_selection.spanned_rows(false, &display_map);
3102 if next_rows.start <= rows.end {
3103 rows.end = next_rows.end;
3104 selections.next().unwrap();
3105 } else {
3106 break;
3107 }
3108 }
3109
3110 let buffer = &display_map.buffer_snapshot;
3111 let mut edit_start = Point::new(rows.start, 0).to_offset(&buffer);
3112 let edit_end;
3113 let cursor_buffer_row;
3114 if buffer.max_point().row >= rows.end {
3115 // If there's a line after the range, delete the \n from the end of the row range
3116 // and position the cursor on the next line.
3117 edit_end = Point::new(rows.end, 0).to_offset(&buffer);
3118 cursor_buffer_row = rows.end;
3119 } else {
3120 // If there isn't a line after the range, delete the \n from the line before the
3121 // start of the row range and position the cursor there.
3122 edit_start = edit_start.saturating_sub(1);
3123 edit_end = buffer.len();
3124 cursor_buffer_row = rows.start.saturating_sub(1);
3125 }
3126
3127 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
3128 *cursor.column_mut() =
3129 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
3130
3131 new_cursors.push((
3132 selection.id,
3133 buffer.anchor_after(cursor.to_point(&display_map)),
3134 ));
3135 edit_ranges.push(edit_start..edit_end);
3136 }
3137
3138 self.transact(cx, |this, cx| {
3139 let buffer = this.buffer.update(cx, |buffer, cx| {
3140 let empty_str: Arc<str> = "".into();
3141 buffer.edit(
3142 edit_ranges
3143 .into_iter()
3144 .map(|range| (range, empty_str.clone())),
3145 cx,
3146 );
3147 buffer.snapshot(cx)
3148 });
3149 let new_selections = new_cursors
3150 .into_iter()
3151 .map(|(id, cursor)| {
3152 let cursor = cursor.to_point(&buffer);
3153 Selection {
3154 id,
3155 start: cursor,
3156 end: cursor,
3157 reversed: false,
3158 goal: SelectionGoal::None,
3159 }
3160 })
3161 .collect();
3162
3163 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3164 s.select(new_selections);
3165 });
3166 });
3167 }
3168
3169 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
3170 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3171 let buffer = &display_map.buffer_snapshot;
3172 let selections = self.selections.all::<Point>(cx);
3173
3174 let mut edits = Vec::new();
3175 let mut selections_iter = selections.iter().peekable();
3176 while let Some(selection) = selections_iter.next() {
3177 // Avoid duplicating the same lines twice.
3178 let mut rows = selection.spanned_rows(false, &display_map);
3179
3180 while let Some(next_selection) = selections_iter.peek() {
3181 let next_rows = next_selection.spanned_rows(false, &display_map);
3182 if next_rows.start <= rows.end - 1 {
3183 rows.end = next_rows.end;
3184 selections_iter.next().unwrap();
3185 } else {
3186 break;
3187 }
3188 }
3189
3190 // Copy the text from the selected row region and splice it at the start of the region.
3191 let start = Point::new(rows.start, 0);
3192 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
3193 let text = buffer
3194 .text_for_range(start..end)
3195 .chain(Some("\n"))
3196 .collect::<String>();
3197 edits.push((start..start, text));
3198 }
3199
3200 self.transact(cx, |this, cx| {
3201 this.buffer.update(cx, |buffer, cx| {
3202 buffer.edit(edits, cx);
3203 });
3204
3205 this.request_autoscroll(Autoscroll::Fit, cx);
3206 });
3207 }
3208
3209 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
3210 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3211 let buffer = self.buffer.read(cx).snapshot(cx);
3212
3213 let mut edits = Vec::new();
3214 let mut unfold_ranges = Vec::new();
3215 let mut refold_ranges = Vec::new();
3216
3217 let selections = self.selections.all::<Point>(cx);
3218 let mut selections = selections.iter().peekable();
3219 let mut contiguous_row_selections = Vec::new();
3220 let mut new_selections = Vec::new();
3221
3222 while let Some(selection) = selections.next() {
3223 // Find all the selections that span a contiguous row range
3224 contiguous_row_selections.push(selection.clone());
3225 let start_row = selection.start.row;
3226 let mut end_row = if selection.end.column > 0 || selection.is_empty() {
3227 display_map.next_line_boundary(selection.end).0.row + 1
3228 } else {
3229 selection.end.row
3230 };
3231
3232 while let Some(next_selection) = selections.peek() {
3233 if next_selection.start.row <= end_row {
3234 end_row = if next_selection.end.column > 0 || next_selection.is_empty() {
3235 display_map.next_line_boundary(next_selection.end).0.row + 1
3236 } else {
3237 next_selection.end.row
3238 };
3239 contiguous_row_selections.push(selections.next().unwrap().clone());
3240 } else {
3241 break;
3242 }
3243 }
3244
3245 // Move the text spanned by the row range to be before the line preceding the row range
3246 if start_row > 0 {
3247 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
3248 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
3249 let insertion_point = display_map
3250 .prev_line_boundary(Point::new(start_row - 1, 0))
3251 .0;
3252
3253 // Don't move lines across excerpts
3254 if buffer
3255 .excerpt_boundaries_in_range((
3256 Bound::Excluded(insertion_point),
3257 Bound::Included(range_to_move.end),
3258 ))
3259 .next()
3260 .is_none()
3261 {
3262 let text = buffer
3263 .text_for_range(range_to_move.clone())
3264 .flat_map(|s| s.chars())
3265 .skip(1)
3266 .chain(['\n'])
3267 .collect::<String>();
3268
3269 edits.push((
3270 buffer.anchor_after(range_to_move.start)
3271 ..buffer.anchor_before(range_to_move.end),
3272 String::new(),
3273 ));
3274 let insertion_anchor = buffer.anchor_after(insertion_point);
3275 edits.push((insertion_anchor.clone()..insertion_anchor, text));
3276
3277 let row_delta = range_to_move.start.row - insertion_point.row + 1;
3278
3279 // Move selections up
3280 new_selections.extend(contiguous_row_selections.drain(..).map(
3281 |mut selection| {
3282 selection.start.row -= row_delta;
3283 selection.end.row -= row_delta;
3284 selection
3285 },
3286 ));
3287
3288 // Move folds up
3289 unfold_ranges.push(range_to_move.clone());
3290 for fold in display_map.folds_in_range(
3291 buffer.anchor_before(range_to_move.start)
3292 ..buffer.anchor_after(range_to_move.end),
3293 ) {
3294 let mut start = fold.start.to_point(&buffer);
3295 let mut end = fold.end.to_point(&buffer);
3296 start.row -= row_delta;
3297 end.row -= row_delta;
3298 refold_ranges.push(start..end);
3299 }
3300 }
3301 }
3302
3303 // If we didn't move line(s), preserve the existing selections
3304 new_selections.extend(contiguous_row_selections.drain(..));
3305 }
3306
3307 self.transact(cx, |this, cx| {
3308 this.unfold_ranges(unfold_ranges, true, cx);
3309 this.buffer.update(cx, |buffer, cx| {
3310 for (range, text) in edits {
3311 buffer.edit([(range, text)], cx);
3312 }
3313 });
3314 this.fold_ranges(refold_ranges, cx);
3315 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3316 s.select(new_selections);
3317 })
3318 });
3319 }
3320
3321 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
3322 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3323 let buffer = self.buffer.read(cx).snapshot(cx);
3324
3325 let mut edits = Vec::new();
3326 let mut unfold_ranges = Vec::new();
3327 let mut refold_ranges = Vec::new();
3328
3329 let selections = self.selections.all::<Point>(cx);
3330 let mut selections = selections.iter().peekable();
3331 let mut contiguous_row_selections = Vec::new();
3332 let mut new_selections = Vec::new();
3333
3334 while let Some(selection) = selections.next() {
3335 // Find all the selections that span a contiguous row range
3336 contiguous_row_selections.push(selection.clone());
3337 let start_row = selection.start.row;
3338 let mut end_row = if selection.end.column > 0 || selection.is_empty() {
3339 display_map.next_line_boundary(selection.end).0.row + 1
3340 } else {
3341 selection.end.row
3342 };
3343
3344 while let Some(next_selection) = selections.peek() {
3345 if next_selection.start.row <= end_row {
3346 end_row = if next_selection.end.column > 0 || next_selection.is_empty() {
3347 display_map.next_line_boundary(next_selection.end).0.row + 1
3348 } else {
3349 next_selection.end.row
3350 };
3351 contiguous_row_selections.push(selections.next().unwrap().clone());
3352 } else {
3353 break;
3354 }
3355 }
3356
3357 // Move the text spanned by the row range to be after the last line of the row range
3358 if end_row <= buffer.max_point().row {
3359 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
3360 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
3361
3362 // Don't move lines across excerpt boundaries
3363 if buffer
3364 .excerpt_boundaries_in_range((
3365 Bound::Excluded(range_to_move.start),
3366 Bound::Included(insertion_point),
3367 ))
3368 .next()
3369 .is_none()
3370 {
3371 let mut text = String::from("\n");
3372 text.extend(buffer.text_for_range(range_to_move.clone()));
3373 text.pop(); // Drop trailing newline
3374 edits.push((
3375 buffer.anchor_after(range_to_move.start)
3376 ..buffer.anchor_before(range_to_move.end),
3377 String::new(),
3378 ));
3379 let insertion_anchor = buffer.anchor_after(insertion_point);
3380 edits.push((insertion_anchor.clone()..insertion_anchor, text));
3381
3382 let row_delta = insertion_point.row - range_to_move.end.row + 1;
3383
3384 // Move selections down
3385 new_selections.extend(contiguous_row_selections.drain(..).map(
3386 |mut selection| {
3387 selection.start.row += row_delta;
3388 selection.end.row += row_delta;
3389 selection
3390 },
3391 ));
3392
3393 // Move folds down
3394 unfold_ranges.push(range_to_move.clone());
3395 for fold in display_map.folds_in_range(
3396 buffer.anchor_before(range_to_move.start)
3397 ..buffer.anchor_after(range_to_move.end),
3398 ) {
3399 let mut start = fold.start.to_point(&buffer);
3400 let mut end = fold.end.to_point(&buffer);
3401 start.row += row_delta;
3402 end.row += row_delta;
3403 refold_ranges.push(start..end);
3404 }
3405 }
3406 }
3407
3408 // If we didn't move line(s), preserve the existing selections
3409 new_selections.extend(contiguous_row_selections.drain(..));
3410 }
3411
3412 self.transact(cx, |this, cx| {
3413 this.unfold_ranges(unfold_ranges, true, cx);
3414 this.buffer.update(cx, |buffer, cx| {
3415 for (range, text) in edits {
3416 buffer.edit([(range, text)], cx);
3417 }
3418 });
3419 this.fold_ranges(refold_ranges, cx);
3420 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(new_selections));
3421 });
3422 }
3423
3424 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
3425 self.transact(cx, |this, cx| {
3426 let edits = this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3427 let mut edits: Vec<(Range<usize>, String)> = Default::default();
3428 let line_mode = s.line_mode;
3429 s.move_with(|display_map, selection| {
3430 if !selection.is_empty() || line_mode {
3431 return;
3432 }
3433
3434 let mut head = selection.head();
3435 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
3436 if head.column() == display_map.line_len(head.row()) {
3437 transpose_offset = display_map
3438 .buffer_snapshot
3439 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
3440 }
3441
3442 if transpose_offset == 0 {
3443 return;
3444 }
3445
3446 *head.column_mut() += 1;
3447 head = display_map.clip_point(head, Bias::Right);
3448 selection.collapse_to(head, SelectionGoal::Column(head.column()));
3449
3450 let transpose_start = display_map
3451 .buffer_snapshot
3452 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
3453 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
3454 let transpose_end = display_map
3455 .buffer_snapshot
3456 .clip_offset(transpose_offset + 1, Bias::Right);
3457 if let Some(ch) =
3458 display_map.buffer_snapshot.chars_at(transpose_start).next()
3459 {
3460 edits.push((transpose_start..transpose_offset, String::new()));
3461 edits.push((transpose_end..transpose_end, ch.to_string()));
3462 }
3463 }
3464 });
3465 edits
3466 });
3467 this.buffer.update(cx, |buffer, cx| buffer.edit(edits, cx));
3468 let selections = this.selections.all::<usize>(cx);
3469 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3470 s.select(selections);
3471 });
3472 });
3473 }
3474
3475 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
3476 let mut text = String::new();
3477 let buffer = self.buffer.read(cx).snapshot(cx);
3478 let mut selections = self.selections.all::<Point>(cx);
3479 let mut clipboard_selections = Vec::with_capacity(selections.len());
3480 {
3481 let max_point = buffer.max_point();
3482 for selection in &mut selections {
3483 let is_entire_line = selection.is_empty() || self.selections.line_mode;
3484 if is_entire_line {
3485 selection.start = Point::new(selection.start.row, 0);
3486 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
3487 selection.goal = SelectionGoal::None;
3488 }
3489 let mut len = 0;
3490 for chunk in buffer.text_for_range(selection.start..selection.end) {
3491 text.push_str(chunk);
3492 len += chunk.len();
3493 }
3494 clipboard_selections.push(ClipboardSelection {
3495 len,
3496 is_entire_line,
3497 });
3498 }
3499 }
3500
3501 self.transact(cx, |this, cx| {
3502 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3503 s.select(selections);
3504 });
3505 this.insert("", cx);
3506 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
3507 });
3508 }
3509
3510 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
3511 let selections = self.selections.all::<Point>(cx);
3512 let buffer = self.buffer.read(cx).read(cx);
3513 let mut text = String::new();
3514
3515 let mut clipboard_selections = Vec::with_capacity(selections.len());
3516 {
3517 let max_point = buffer.max_point();
3518 for selection in selections.iter() {
3519 let mut start = selection.start;
3520 let mut end = selection.end;
3521 let is_entire_line = selection.is_empty() || self.selections.line_mode;
3522 if is_entire_line {
3523 start = Point::new(start.row, 0);
3524 end = cmp::min(max_point, Point::new(end.row + 1, 0));
3525 }
3526 let mut len = 0;
3527 for chunk in buffer.text_for_range(start..end) {
3528 text.push_str(chunk);
3529 len += chunk.len();
3530 }
3531 clipboard_selections.push(ClipboardSelection {
3532 len,
3533 is_entire_line,
3534 });
3535 }
3536 }
3537
3538 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
3539 }
3540
3541 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
3542 self.transact(cx, |this, cx| {
3543 if let Some(item) = cx.as_mut().read_from_clipboard() {
3544 let mut clipboard_text = Cow::Borrowed(item.text());
3545 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
3546 let old_selections = this.selections.all::<usize>(cx);
3547 let all_selections_were_entire_line =
3548 clipboard_selections.iter().all(|s| s.is_entire_line);
3549 if clipboard_selections.len() != old_selections.len() {
3550 let mut newline_separated_text = String::new();
3551 let mut clipboard_selections = clipboard_selections.drain(..).peekable();
3552 let mut ix = 0;
3553 while let Some(clipboard_selection) = clipboard_selections.next() {
3554 newline_separated_text
3555 .push_str(&clipboard_text[ix..ix + clipboard_selection.len]);
3556 ix += clipboard_selection.len;
3557 if clipboard_selections.peek().is_some() {
3558 newline_separated_text.push('\n');
3559 }
3560 }
3561 clipboard_text = Cow::Owned(newline_separated_text);
3562 }
3563
3564 this.buffer.update(cx, |buffer, cx| {
3565 let snapshot = buffer.read(cx);
3566 let mut start_offset = 0;
3567 let mut edits = Vec::new();
3568 let line_mode = this.selections.line_mode;
3569 for (ix, selection) in old_selections.iter().enumerate() {
3570 let to_insert;
3571 let entire_line;
3572 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
3573 let end_offset = start_offset + clipboard_selection.len;
3574 to_insert = &clipboard_text[start_offset..end_offset];
3575 entire_line = clipboard_selection.is_entire_line;
3576 start_offset = end_offset;
3577 } else {
3578 to_insert = clipboard_text.as_str();
3579 entire_line = all_selections_were_entire_line;
3580 }
3581
3582 // If the corresponding selection was empty when this slice of the
3583 // clipboard text was written, then the entire line containing the
3584 // selection was copied. If this selection is also currently empty,
3585 // then paste the line before the current line of the buffer.
3586 let range = if selection.is_empty() && !line_mode && entire_line {
3587 let column = selection.start.to_point(&snapshot).column as usize;
3588 let line_start = selection.start - column;
3589 line_start..line_start
3590 } else {
3591 selection.range()
3592 };
3593
3594 edits.push((range, to_insert));
3595 }
3596 drop(snapshot);
3597 buffer.edit_with_autoindent(edits, cx);
3598 });
3599
3600 let selections = this.selections.all::<usize>(cx);
3601 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
3602 } else {
3603 this.insert(&clipboard_text, cx);
3604 }
3605 }
3606 });
3607 }
3608
3609 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
3610 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
3611 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
3612 self.change_selections(None, cx, |s| {
3613 s.select_anchors(selections.to_vec());
3614 });
3615 }
3616 self.request_autoscroll(Autoscroll::Fit, cx);
3617 cx.emit(Event::Edited);
3618 }
3619 }
3620
3621 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
3622 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
3623 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
3624 {
3625 self.change_selections(None, cx, |s| {
3626 s.select_anchors(selections.to_vec());
3627 });
3628 }
3629 self.request_autoscroll(Autoscroll::Fit, cx);
3630 cx.emit(Event::Edited);
3631 }
3632 }
3633
3634 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
3635 self.buffer
3636 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
3637 }
3638
3639 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
3640 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3641 let line_mode = s.line_mode;
3642 s.move_with(|map, selection| {
3643 let cursor = if selection.is_empty() && !line_mode {
3644 movement::left(map, selection.start)
3645 } else {
3646 selection.start
3647 };
3648 selection.collapse_to(cursor, SelectionGoal::None);
3649 });
3650 })
3651 }
3652
3653 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
3654 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3655 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
3656 })
3657 }
3658
3659 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
3660 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3661 let line_mode = s.line_mode;
3662 s.move_with(|map, selection| {
3663 let cursor = if selection.is_empty() && !line_mode {
3664 movement::right(map, selection.end)
3665 } else {
3666 selection.end
3667 };
3668 selection.collapse_to(cursor, SelectionGoal::None)
3669 });
3670 })
3671 }
3672
3673 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
3674 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3675 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
3676 })
3677 }
3678
3679 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
3680 if self.take_rename(true, cx).is_some() {
3681 return;
3682 }
3683
3684 if let Some(context_menu) = self.context_menu.as_mut() {
3685 if context_menu.select_prev(cx) {
3686 return;
3687 }
3688 }
3689
3690 if matches!(self.mode, EditorMode::SingleLine) {
3691 cx.propagate_action();
3692 return;
3693 }
3694
3695 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3696 let line_mode = s.line_mode;
3697 s.move_with(|map, selection| {
3698 if !selection.is_empty() && !line_mode {
3699 selection.goal = SelectionGoal::None;
3700 }
3701 let (cursor, goal) = movement::up(&map, selection.start, selection.goal, false);
3702 selection.collapse_to(cursor, goal);
3703 });
3704 })
3705 }
3706
3707 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
3708 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3709 s.move_heads_with(|map, head, goal| movement::up(map, head, goal, false))
3710 })
3711 }
3712
3713 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
3714 self.take_rename(true, cx);
3715
3716 if let Some(context_menu) = self.context_menu.as_mut() {
3717 if context_menu.select_next(cx) {
3718 return;
3719 }
3720 }
3721
3722 if matches!(self.mode, EditorMode::SingleLine) {
3723 cx.propagate_action();
3724 return;
3725 }
3726
3727 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3728 let line_mode = s.line_mode;
3729 s.move_with(|map, selection| {
3730 if !selection.is_empty() && !line_mode {
3731 selection.goal = SelectionGoal::None;
3732 }
3733 let (cursor, goal) = movement::down(&map, selection.end, selection.goal, false);
3734 selection.collapse_to(cursor, goal);
3735 });
3736 });
3737 }
3738
3739 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
3740 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3741 s.move_heads_with(|map, head, goal| movement::down(map, head, goal, false))
3742 });
3743 }
3744
3745 pub fn move_to_previous_word_start(
3746 &mut self,
3747 _: &MoveToPreviousWordStart,
3748 cx: &mut ViewContext<Self>,
3749 ) {
3750 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3751 s.move_cursors_with(|map, head, _| {
3752 (
3753 movement::previous_word_start(map, head),
3754 SelectionGoal::None,
3755 )
3756 });
3757 })
3758 }
3759
3760 pub fn move_to_previous_subword_start(
3761 &mut self,
3762 _: &MoveToPreviousSubwordStart,
3763 cx: &mut ViewContext<Self>,
3764 ) {
3765 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3766 s.move_cursors_with(|map, head, _| {
3767 (
3768 movement::previous_subword_start(map, head),
3769 SelectionGoal::None,
3770 )
3771 });
3772 })
3773 }
3774
3775 pub fn select_to_previous_word_start(
3776 &mut self,
3777 _: &SelectToPreviousWordStart,
3778 cx: &mut ViewContext<Self>,
3779 ) {
3780 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3781 s.move_heads_with(|map, head, _| {
3782 (
3783 movement::previous_word_start(map, head),
3784 SelectionGoal::None,
3785 )
3786 });
3787 })
3788 }
3789
3790 pub fn select_to_previous_subword_start(
3791 &mut self,
3792 _: &SelectToPreviousSubwordStart,
3793 cx: &mut ViewContext<Self>,
3794 ) {
3795 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3796 s.move_heads_with(|map, head, _| {
3797 (
3798 movement::previous_subword_start(map, head),
3799 SelectionGoal::None,
3800 )
3801 });
3802 })
3803 }
3804
3805 pub fn delete_to_previous_word_start(
3806 &mut self,
3807 _: &DeleteToPreviousWordStart,
3808 cx: &mut ViewContext<Self>,
3809 ) {
3810 self.transact(cx, |this, cx| {
3811 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3812 let line_mode = s.line_mode;
3813 s.move_with(|map, selection| {
3814 if selection.is_empty() && !line_mode {
3815 let cursor = movement::previous_word_start(map, selection.head());
3816 selection.set_head(cursor, SelectionGoal::None);
3817 }
3818 });
3819 });
3820 this.insert("", cx);
3821 });
3822 }
3823
3824 pub fn delete_to_previous_subword_start(
3825 &mut self,
3826 _: &DeleteToPreviousSubwordStart,
3827 cx: &mut ViewContext<Self>,
3828 ) {
3829 self.transact(cx, |this, cx| {
3830 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3831 let line_mode = s.line_mode;
3832 s.move_with(|map, selection| {
3833 if selection.is_empty() && !line_mode {
3834 let cursor = movement::previous_subword_start(map, selection.head());
3835 selection.set_head(cursor, SelectionGoal::None);
3836 }
3837 });
3838 });
3839 this.insert("", cx);
3840 });
3841 }
3842
3843 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
3844 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3845 s.move_cursors_with(|map, head, _| {
3846 (movement::next_word_end(map, head), SelectionGoal::None)
3847 });
3848 })
3849 }
3850
3851 pub fn move_to_next_subword_end(
3852 &mut self,
3853 _: &MoveToNextSubwordEnd,
3854 cx: &mut ViewContext<Self>,
3855 ) {
3856 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3857 s.move_cursors_with(|map, head, _| {
3858 (movement::next_subword_end(map, head), SelectionGoal::None)
3859 });
3860 })
3861 }
3862
3863 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
3864 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3865 s.move_heads_with(|map, head, _| {
3866 (movement::next_word_end(map, head), SelectionGoal::None)
3867 });
3868 })
3869 }
3870
3871 pub fn select_to_next_subword_end(
3872 &mut self,
3873 _: &SelectToNextSubwordEnd,
3874 cx: &mut ViewContext<Self>,
3875 ) {
3876 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3877 s.move_heads_with(|map, head, _| {
3878 (movement::next_subword_end(map, head), SelectionGoal::None)
3879 });
3880 })
3881 }
3882
3883 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
3884 self.transact(cx, |this, cx| {
3885 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3886 let line_mode = s.line_mode;
3887 s.move_with(|map, selection| {
3888 if selection.is_empty() && !line_mode {
3889 let cursor = movement::next_word_end(map, selection.head());
3890 selection.set_head(cursor, SelectionGoal::None);
3891 }
3892 });
3893 });
3894 this.insert("", cx);
3895 });
3896 }
3897
3898 pub fn delete_to_next_subword_end(
3899 &mut self,
3900 _: &DeleteToNextSubwordEnd,
3901 cx: &mut ViewContext<Self>,
3902 ) {
3903 self.transact(cx, |this, cx| {
3904 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3905 s.move_with(|map, selection| {
3906 if selection.is_empty() {
3907 let cursor = movement::next_subword_end(map, selection.head());
3908 selection.set_head(cursor, SelectionGoal::None);
3909 }
3910 });
3911 });
3912 this.insert("", cx);
3913 });
3914 }
3915
3916 pub fn move_to_beginning_of_line(
3917 &mut self,
3918 _: &MoveToBeginningOfLine,
3919 cx: &mut ViewContext<Self>,
3920 ) {
3921 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3922 s.move_cursors_with(|map, head, _| {
3923 (
3924 movement::line_beginning(map, head, true),
3925 SelectionGoal::None,
3926 )
3927 });
3928 })
3929 }
3930
3931 pub fn select_to_beginning_of_line(
3932 &mut self,
3933 action: &SelectToBeginningOfLine,
3934 cx: &mut ViewContext<Self>,
3935 ) {
3936 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3937 s.move_heads_with(|map, head, _| {
3938 (
3939 movement::line_beginning(map, head, action.stop_at_soft_wraps),
3940 SelectionGoal::None,
3941 )
3942 });
3943 });
3944 }
3945
3946 pub fn delete_to_beginning_of_line(
3947 &mut self,
3948 _: &DeleteToBeginningOfLine,
3949 cx: &mut ViewContext<Self>,
3950 ) {
3951 self.transact(cx, |this, cx| {
3952 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3953 s.move_with(|_, selection| {
3954 selection.reversed = true;
3955 });
3956 });
3957
3958 this.select_to_beginning_of_line(
3959 &SelectToBeginningOfLine {
3960 stop_at_soft_wraps: false,
3961 },
3962 cx,
3963 );
3964 this.backspace(&Backspace, cx);
3965 });
3966 }
3967
3968 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
3969 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3970 s.move_cursors_with(|map, head, _| {
3971 (movement::line_end(map, head, true), SelectionGoal::None)
3972 });
3973 })
3974 }
3975
3976 pub fn select_to_end_of_line(
3977 &mut self,
3978 action: &SelectToEndOfLine,
3979 cx: &mut ViewContext<Self>,
3980 ) {
3981 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3982 s.move_heads_with(|map, head, _| {
3983 (
3984 movement::line_end(map, head, action.stop_at_soft_wraps),
3985 SelectionGoal::None,
3986 )
3987 });
3988 })
3989 }
3990
3991 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
3992 self.transact(cx, |this, cx| {
3993 this.select_to_end_of_line(
3994 &SelectToEndOfLine {
3995 stop_at_soft_wraps: false,
3996 },
3997 cx,
3998 );
3999 this.delete(&Delete, cx);
4000 });
4001 }
4002
4003 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
4004 self.transact(cx, |this, cx| {
4005 this.select_to_end_of_line(
4006 &SelectToEndOfLine {
4007 stop_at_soft_wraps: false,
4008 },
4009 cx,
4010 );
4011 this.cut(&Cut, cx);
4012 });
4013 }
4014
4015 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
4016 if matches!(self.mode, EditorMode::SingleLine) {
4017 cx.propagate_action();
4018 return;
4019 }
4020
4021 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4022 s.select_ranges(vec![0..0]);
4023 });
4024 }
4025
4026 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
4027 let mut selection = self.selections.last::<Point>(cx);
4028 selection.set_head(Point::zero(), SelectionGoal::None);
4029
4030 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4031 s.select(vec![selection]);
4032 });
4033 }
4034
4035 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
4036 if matches!(self.mode, EditorMode::SingleLine) {
4037 cx.propagate_action();
4038 return;
4039 }
4040
4041 let cursor = self.buffer.read(cx).read(cx).len();
4042 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4043 s.select_ranges(vec![cursor..cursor])
4044 });
4045 }
4046
4047 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
4048 self.nav_history = nav_history;
4049 }
4050
4051 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
4052 self.nav_history.as_ref()
4053 }
4054
4055 fn push_to_nav_history(
4056 &self,
4057 position: Anchor,
4058 new_position: Option<Point>,
4059 cx: &mut ViewContext<Self>,
4060 ) {
4061 if let Some(nav_history) = &self.nav_history {
4062 let buffer = self.buffer.read(cx).read(cx);
4063 let point = position.to_point(&buffer);
4064 let scroll_top_row = self.scroll_top_anchor.to_point(&buffer).row;
4065 drop(buffer);
4066
4067 if let Some(new_position) = new_position {
4068 let row_delta = (new_position.row as i64 - point.row as i64).abs();
4069 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
4070 return;
4071 }
4072 }
4073
4074 nav_history.push(Some(NavigationData {
4075 cursor_anchor: position,
4076 cursor_position: point,
4077 scroll_position: self.scroll_position,
4078 scroll_top_anchor: self.scroll_top_anchor.clone(),
4079 scroll_top_row,
4080 }));
4081 }
4082 }
4083
4084 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
4085 let buffer = self.buffer.read(cx).snapshot(cx);
4086 let mut selection = self.selections.first::<usize>(cx);
4087 selection.set_head(buffer.len(), SelectionGoal::None);
4088 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4089 s.select(vec![selection]);
4090 });
4091 }
4092
4093 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
4094 let end = self.buffer.read(cx).read(cx).len();
4095 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4096 s.select_ranges(vec![0..end]);
4097 });
4098 }
4099
4100 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
4101 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4102 let mut selections = self.selections.all::<Point>(cx);
4103 let max_point = display_map.buffer_snapshot.max_point();
4104 for selection in &mut selections {
4105 let rows = selection.spanned_rows(true, &display_map);
4106 selection.start = Point::new(rows.start, 0);
4107 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
4108 selection.reversed = false;
4109 }
4110 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4111 s.select(selections);
4112 });
4113 }
4114
4115 pub fn split_selection_into_lines(
4116 &mut self,
4117 _: &SplitSelectionIntoLines,
4118 cx: &mut ViewContext<Self>,
4119 ) {
4120 let mut to_unfold = Vec::new();
4121 let mut new_selection_ranges = Vec::new();
4122 {
4123 let selections = self.selections.all::<Point>(cx);
4124 let buffer = self.buffer.read(cx).read(cx);
4125 for selection in selections {
4126 for row in selection.start.row..selection.end.row {
4127 let cursor = Point::new(row, buffer.line_len(row));
4128 new_selection_ranges.push(cursor..cursor);
4129 }
4130 new_selection_ranges.push(selection.end..selection.end);
4131 to_unfold.push(selection.start..selection.end);
4132 }
4133 }
4134 self.unfold_ranges(to_unfold, true, cx);
4135 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4136 s.select_ranges(new_selection_ranges);
4137 });
4138 }
4139
4140 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
4141 self.add_selection(true, cx);
4142 }
4143
4144 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
4145 self.add_selection(false, cx);
4146 }
4147
4148 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
4149 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4150 let mut selections = self.selections.all::<Point>(cx);
4151 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
4152 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
4153 let range = oldest_selection.display_range(&display_map).sorted();
4154 let columns = cmp::min(range.start.column(), range.end.column())
4155 ..cmp::max(range.start.column(), range.end.column());
4156
4157 selections.clear();
4158 let mut stack = Vec::new();
4159 for row in range.start.row()..=range.end.row() {
4160 if let Some(selection) = self.selections.build_columnar_selection(
4161 &display_map,
4162 row,
4163 &columns,
4164 oldest_selection.reversed,
4165 ) {
4166 stack.push(selection.id);
4167 selections.push(selection);
4168 }
4169 }
4170
4171 if above {
4172 stack.reverse();
4173 }
4174
4175 AddSelectionsState { above, stack }
4176 });
4177
4178 let last_added_selection = *state.stack.last().unwrap();
4179 let mut new_selections = Vec::new();
4180 if above == state.above {
4181 let end_row = if above {
4182 0
4183 } else {
4184 display_map.max_point().row()
4185 };
4186
4187 'outer: for selection in selections {
4188 if selection.id == last_added_selection {
4189 let range = selection.display_range(&display_map).sorted();
4190 debug_assert_eq!(range.start.row(), range.end.row());
4191 let mut row = range.start.row();
4192 let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
4193 {
4194 start..end
4195 } else {
4196 cmp::min(range.start.column(), range.end.column())
4197 ..cmp::max(range.start.column(), range.end.column())
4198 };
4199
4200 while row != end_row {
4201 if above {
4202 row -= 1;
4203 } else {
4204 row += 1;
4205 }
4206
4207 if let Some(new_selection) = self.selections.build_columnar_selection(
4208 &display_map,
4209 row,
4210 &columns,
4211 selection.reversed,
4212 ) {
4213 state.stack.push(new_selection.id);
4214 if above {
4215 new_selections.push(new_selection);
4216 new_selections.push(selection);
4217 } else {
4218 new_selections.push(selection);
4219 new_selections.push(new_selection);
4220 }
4221
4222 continue 'outer;
4223 }
4224 }
4225 }
4226
4227 new_selections.push(selection);
4228 }
4229 } else {
4230 new_selections = selections;
4231 new_selections.retain(|s| s.id != last_added_selection);
4232 state.stack.pop();
4233 }
4234
4235 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4236 s.select(new_selections);
4237 });
4238 if state.stack.len() > 1 {
4239 self.add_selections_state = Some(state);
4240 }
4241 }
4242
4243 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) {
4244 self.push_to_selection_history();
4245 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4246 let buffer = &display_map.buffer_snapshot;
4247 let mut selections = self.selections.all::<usize>(cx);
4248 if let Some(mut select_next_state) = self.select_next_state.take() {
4249 let query = &select_next_state.query;
4250 if !select_next_state.done {
4251 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
4252 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
4253 let mut next_selected_range = None;
4254
4255 let bytes_after_last_selection =
4256 buffer.bytes_in_range(last_selection.end..buffer.len());
4257 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
4258 let query_matches = query
4259 .stream_find_iter(bytes_after_last_selection)
4260 .map(|result| (last_selection.end, result))
4261 .chain(
4262 query
4263 .stream_find_iter(bytes_before_first_selection)
4264 .map(|result| (0, result)),
4265 );
4266 for (start_offset, query_match) in query_matches {
4267 let query_match = query_match.unwrap(); // can only fail due to I/O
4268 let offset_range =
4269 start_offset + query_match.start()..start_offset + query_match.end();
4270 let display_range = offset_range.start.to_display_point(&display_map)
4271 ..offset_range.end.to_display_point(&display_map);
4272
4273 if !select_next_state.wordwise
4274 || (!movement::is_inside_word(&display_map, display_range.start)
4275 && !movement::is_inside_word(&display_map, display_range.end))
4276 {
4277 next_selected_range = Some(offset_range);
4278 break;
4279 }
4280 }
4281
4282 if let Some(next_selected_range) = next_selected_range {
4283 self.unfold_ranges([next_selected_range.clone()], false, cx);
4284 self.change_selections(Some(Autoscroll::Newest), cx, |s| {
4285 if action.replace_newest {
4286 s.delete(s.newest_anchor().id);
4287 }
4288 s.insert_range(next_selected_range);
4289 });
4290 } else {
4291 select_next_state.done = true;
4292 }
4293 }
4294
4295 self.select_next_state = Some(select_next_state);
4296 } else if selections.len() == 1 {
4297 let selection = selections.last_mut().unwrap();
4298 if selection.start == selection.end {
4299 let word_range = movement::surrounding_word(
4300 &display_map,
4301 selection.start.to_display_point(&display_map),
4302 );
4303 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
4304 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
4305 selection.goal = SelectionGoal::None;
4306 selection.reversed = false;
4307
4308 let query = buffer
4309 .text_for_range(selection.start..selection.end)
4310 .collect::<String>();
4311 let select_state = SelectNextState {
4312 query: AhoCorasick::new_auto_configured(&[query]),
4313 wordwise: true,
4314 done: false,
4315 };
4316 self.unfold_ranges([selection.start..selection.end], false, cx);
4317 self.change_selections(Some(Autoscroll::Newest), cx, |s| {
4318 s.select(selections);
4319 });
4320 self.select_next_state = Some(select_state);
4321 } else {
4322 let query = buffer
4323 .text_for_range(selection.start..selection.end)
4324 .collect::<String>();
4325 self.select_next_state = Some(SelectNextState {
4326 query: AhoCorasick::new_auto_configured(&[query]),
4327 wordwise: false,
4328 done: false,
4329 });
4330 self.select_next(action, cx);
4331 }
4332 }
4333 }
4334
4335 pub fn toggle_comments(&mut self, _: &ToggleComments, cx: &mut ViewContext<Self>) {
4336 self.transact(cx, |this, cx| {
4337 let mut selections = this.selections.all::<Point>(cx);
4338 let mut all_selection_lines_are_comments = true;
4339 let mut edit_ranges = Vec::new();
4340 let mut last_toggled_row = None;
4341 this.buffer.update(cx, |buffer, cx| {
4342 // TODO: Handle selections that cross excerpts
4343 for selection in &mut selections {
4344 // Get the line comment prefix. Split its trailing whitespace into a separate string,
4345 // as that portion won't be used for detecting if a line is a comment.
4346 let full_comment_prefix: Arc<str> = if let Some(prefix) = buffer
4347 .language_at(selection.start, cx)
4348 .and_then(|l| l.line_comment_prefix())
4349 {
4350 prefix.into()
4351 } else {
4352 return;
4353 };
4354 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
4355 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
4356 edit_ranges.clear();
4357 let snapshot = buffer.snapshot(cx);
4358
4359 let end_row =
4360 if selection.end.row > selection.start.row && selection.end.column == 0 {
4361 selection.end.row
4362 } else {
4363 selection.end.row + 1
4364 };
4365
4366 for row in selection.start.row..end_row {
4367 // If multiple selections contain a given row, avoid processing that
4368 // row more than once.
4369 if last_toggled_row == Some(row) {
4370 continue;
4371 } else {
4372 last_toggled_row = Some(row);
4373 }
4374
4375 if snapshot.is_line_blank(row) {
4376 continue;
4377 }
4378
4379 let start = Point::new(row, snapshot.indent_column_for_line(row));
4380 let mut line_bytes = snapshot
4381 .bytes_in_range(start..snapshot.max_point())
4382 .flatten()
4383 .copied();
4384
4385 // If this line currently begins with the line comment prefix, then record
4386 // the range containing the prefix.
4387 if all_selection_lines_are_comments
4388 && line_bytes
4389 .by_ref()
4390 .take(comment_prefix.len())
4391 .eq(comment_prefix.bytes())
4392 {
4393 // Include any whitespace that matches the comment prefix.
4394 let matching_whitespace_len = line_bytes
4395 .zip(comment_prefix_whitespace.bytes())
4396 .take_while(|(a, b)| a == b)
4397 .count()
4398 as u32;
4399 let end = Point::new(
4400 row,
4401 start.column
4402 + comment_prefix.len() as u32
4403 + matching_whitespace_len,
4404 );
4405 edit_ranges.push(start..end);
4406 }
4407 // If this line does not begin with the line comment prefix, then record
4408 // the position where the prefix should be inserted.
4409 else {
4410 all_selection_lines_are_comments = false;
4411 edit_ranges.push(start..start);
4412 }
4413 }
4414
4415 if !edit_ranges.is_empty() {
4416 if all_selection_lines_are_comments {
4417 let empty_str: Arc<str> = "".into();
4418 buffer.edit(
4419 edit_ranges
4420 .iter()
4421 .cloned()
4422 .map(|range| (range, empty_str.clone())),
4423 cx,
4424 );
4425 } else {
4426 let min_column =
4427 edit_ranges.iter().map(|r| r.start.column).min().unwrap();
4428 let edits = edit_ranges.iter().map(|range| {
4429 let position = Point::new(range.start.row, min_column);
4430 (position..position, full_comment_prefix.clone())
4431 });
4432 buffer.edit(edits, cx);
4433 }
4434 }
4435 }
4436 });
4437
4438 let selections = this.selections.all::<usize>(cx);
4439 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
4440 });
4441 }
4442
4443 pub fn select_larger_syntax_node(
4444 &mut self,
4445 _: &SelectLargerSyntaxNode,
4446 cx: &mut ViewContext<Self>,
4447 ) {
4448 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4449 let buffer = self.buffer.read(cx).snapshot(cx);
4450 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
4451
4452 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
4453 let mut selected_larger_node = false;
4454 let new_selections = old_selections
4455 .iter()
4456 .map(|selection| {
4457 let old_range = selection.start..selection.end;
4458 let mut new_range = old_range.clone();
4459 while let Some(containing_range) =
4460 buffer.range_for_syntax_ancestor(new_range.clone())
4461 {
4462 new_range = containing_range;
4463 if !display_map.intersects_fold(new_range.start)
4464 && !display_map.intersects_fold(new_range.end)
4465 {
4466 break;
4467 }
4468 }
4469
4470 selected_larger_node |= new_range != old_range;
4471 Selection {
4472 id: selection.id,
4473 start: new_range.start,
4474 end: new_range.end,
4475 goal: SelectionGoal::None,
4476 reversed: selection.reversed,
4477 }
4478 })
4479 .collect::<Vec<_>>();
4480
4481 if selected_larger_node {
4482 stack.push(old_selections);
4483 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4484 s.select(new_selections);
4485 });
4486 }
4487 self.select_larger_syntax_node_stack = stack;
4488 }
4489
4490 pub fn select_smaller_syntax_node(
4491 &mut self,
4492 _: &SelectSmallerSyntaxNode,
4493 cx: &mut ViewContext<Self>,
4494 ) {
4495 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
4496 if let Some(selections) = stack.pop() {
4497 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4498 s.select(selections.to_vec());
4499 });
4500 }
4501 self.select_larger_syntax_node_stack = stack;
4502 }
4503
4504 pub fn move_to_enclosing_bracket(
4505 &mut self,
4506 _: &MoveToEnclosingBracket,
4507 cx: &mut ViewContext<Self>,
4508 ) {
4509 let buffer = self.buffer.read(cx).snapshot(cx);
4510 let mut selections = self.selections.all::<usize>(cx);
4511 for selection in &mut selections {
4512 if let Some((open_range, close_range)) =
4513 buffer.enclosing_bracket_ranges(selection.start..selection.end)
4514 {
4515 let close_range = close_range.to_inclusive();
4516 let destination = if close_range.contains(&selection.start)
4517 && close_range.contains(&selection.end)
4518 {
4519 open_range.end
4520 } else {
4521 *close_range.start()
4522 };
4523 selection.start = destination;
4524 selection.end = destination;
4525 }
4526 }
4527
4528 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4529 s.select(selections);
4530 });
4531 }
4532
4533 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
4534 self.end_selection(cx);
4535 self.selection_history.mode = SelectionHistoryMode::Undoing;
4536 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
4537 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
4538 self.select_next_state = entry.select_next_state;
4539 self.add_selections_state = entry.add_selections_state;
4540 self.request_autoscroll(Autoscroll::Newest, cx);
4541 }
4542 self.selection_history.mode = SelectionHistoryMode::Normal;
4543 }
4544
4545 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
4546 self.end_selection(cx);
4547 self.selection_history.mode = SelectionHistoryMode::Redoing;
4548 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
4549 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
4550 self.select_next_state = entry.select_next_state;
4551 self.add_selections_state = entry.add_selections_state;
4552 self.request_autoscroll(Autoscroll::Newest, cx);
4553 }
4554 self.selection_history.mode = SelectionHistoryMode::Normal;
4555 }
4556
4557 fn go_to_next_diagnostic(&mut self, _: &GoToNextDiagnostic, cx: &mut ViewContext<Self>) {
4558 self.go_to_diagnostic(Direction::Next, cx)
4559 }
4560
4561 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
4562 self.go_to_diagnostic(Direction::Prev, cx)
4563 }
4564
4565 pub fn go_to_diagnostic(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
4566 let buffer = self.buffer.read(cx).snapshot(cx);
4567 let selection = self.selections.newest::<usize>(cx);
4568 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
4569 active_diagnostics
4570 .primary_range
4571 .to_offset(&buffer)
4572 .to_inclusive()
4573 });
4574 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
4575 if active_primary_range.contains(&selection.head()) {
4576 *active_primary_range.end()
4577 } else {
4578 selection.head()
4579 }
4580 } else {
4581 selection.head()
4582 };
4583
4584 loop {
4585 let mut diagnostics = if direction == Direction::Prev {
4586 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
4587 } else {
4588 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
4589 };
4590 let group = diagnostics.find_map(|entry| {
4591 if entry.diagnostic.is_primary
4592 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
4593 && !entry.range.is_empty()
4594 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
4595 {
4596 Some((entry.range, entry.diagnostic.group_id))
4597 } else {
4598 None
4599 }
4600 });
4601
4602 if let Some((primary_range, group_id)) = group {
4603 self.activate_diagnostics(group_id, cx);
4604 self.change_selections(Some(Autoscroll::Center), cx, |s| {
4605 s.select(vec![Selection {
4606 id: selection.id,
4607 start: primary_range.start,
4608 end: primary_range.start,
4609 reversed: false,
4610 goal: SelectionGoal::None,
4611 }]);
4612 });
4613 break;
4614 } else {
4615 // Cycle around to the start of the buffer, potentially moving back to the start of
4616 // the currently active diagnostic.
4617 active_primary_range.take();
4618 if direction == Direction::Prev {
4619 if search_start == buffer.len() {
4620 break;
4621 } else {
4622 search_start = buffer.len();
4623 }
4624 } else {
4625 if search_start == 0 {
4626 break;
4627 } else {
4628 search_start = 0;
4629 }
4630 }
4631 }
4632 }
4633 }
4634
4635 pub fn go_to_definition(
4636 workspace: &mut Workspace,
4637 _: &GoToDefinition,
4638 cx: &mut ViewContext<Workspace>,
4639 ) {
4640 let active_item = workspace.active_item(cx);
4641 let editor_handle = if let Some(editor) = active_item
4642 .as_ref()
4643 .and_then(|item| item.act_as::<Self>(cx))
4644 {
4645 editor
4646 } else {
4647 return;
4648 };
4649
4650 let editor = editor_handle.read(cx);
4651 let buffer = editor.buffer.read(cx);
4652 let head = editor.selections.newest::<usize>(cx).head();
4653 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
4654 text_anchor
4655 } else {
4656 return;
4657 };
4658
4659 let project = workspace.project().clone();
4660 let definitions = project.update(cx, |project, cx| project.definition(&buffer, head, cx));
4661 cx.spawn(|workspace, mut cx| async move {
4662 let definitions = definitions.await?;
4663 workspace.update(&mut cx, |workspace, cx| {
4664 let nav_history = workspace.active_pane().read(cx).nav_history().clone();
4665 for definition in definitions {
4666 let range = definition.range.to_offset(definition.buffer.read(cx));
4667
4668 let target_editor_handle = workspace.open_project_item(definition.buffer, cx);
4669 target_editor_handle.update(cx, |target_editor, cx| {
4670 // When selecting a definition in a different buffer, disable the nav history
4671 // to avoid creating a history entry at the previous cursor location.
4672 if editor_handle != target_editor_handle {
4673 nav_history.borrow_mut().disable();
4674 }
4675 target_editor.change_selections(Some(Autoscroll::Center), cx, |s| {
4676 s.select_ranges([range]);
4677 });
4678
4679 nav_history.borrow_mut().enable();
4680 });
4681 }
4682 });
4683
4684 Ok::<(), anyhow::Error>(())
4685 })
4686 .detach_and_log_err(cx);
4687 }
4688
4689 pub fn find_all_references(
4690 workspace: &mut Workspace,
4691 _: &FindAllReferences,
4692 cx: &mut ViewContext<Workspace>,
4693 ) -> Option<Task<Result<()>>> {
4694 let active_item = workspace.active_item(cx)?;
4695 let editor_handle = active_item.act_as::<Self>(cx)?;
4696
4697 let editor = editor_handle.read(cx);
4698 let buffer = editor.buffer.read(cx);
4699 let head = editor.selections.newest::<usize>(cx).head();
4700 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
4701 let replica_id = editor.replica_id(cx);
4702
4703 let project = workspace.project().clone();
4704 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
4705 Some(cx.spawn(|workspace, mut cx| async move {
4706 let mut locations = references.await?;
4707 if locations.is_empty() {
4708 return Ok(());
4709 }
4710
4711 locations.sort_by_key(|location| location.buffer.id());
4712 let mut locations = locations.into_iter().peekable();
4713 let mut ranges_to_highlight = Vec::new();
4714
4715 let excerpt_buffer = cx.add_model(|cx| {
4716 let mut symbol_name = None;
4717 let mut multibuffer = MultiBuffer::new(replica_id);
4718 while let Some(location) = locations.next() {
4719 let buffer = location.buffer.read(cx);
4720 let mut ranges_for_buffer = Vec::new();
4721 let range = location.range.to_offset(buffer);
4722 ranges_for_buffer.push(range.clone());
4723 if symbol_name.is_none() {
4724 symbol_name = Some(buffer.text_for_range(range).collect::<String>());
4725 }
4726
4727 while let Some(next_location) = locations.peek() {
4728 if next_location.buffer == location.buffer {
4729 ranges_for_buffer.push(next_location.range.to_offset(buffer));
4730 locations.next();
4731 } else {
4732 break;
4733 }
4734 }
4735
4736 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
4737 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
4738 location.buffer.clone(),
4739 ranges_for_buffer,
4740 1,
4741 cx,
4742 ));
4743 }
4744 multibuffer.with_title(format!("References to `{}`", symbol_name.unwrap()))
4745 });
4746
4747 workspace.update(&mut cx, |workspace, cx| {
4748 let editor =
4749 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
4750 editor.update(cx, |editor, cx| {
4751 editor.highlight_background::<Self>(
4752 ranges_to_highlight,
4753 |theme| theme.editor.highlighted_line_background,
4754 cx,
4755 );
4756 });
4757 workspace.add_item(Box::new(editor), cx);
4758 });
4759
4760 Ok(())
4761 }))
4762 }
4763
4764 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
4765 use language::ToOffset as _;
4766
4767 let project = self.project.clone()?;
4768 let selection = self.selections.newest_anchor().clone();
4769 let (cursor_buffer, cursor_buffer_position) = self
4770 .buffer
4771 .read(cx)
4772 .text_anchor_for_position(selection.head(), cx)?;
4773 let (tail_buffer, _) = self
4774 .buffer
4775 .read(cx)
4776 .text_anchor_for_position(selection.tail(), cx)?;
4777 if tail_buffer != cursor_buffer {
4778 return None;
4779 }
4780
4781 let snapshot = cursor_buffer.read(cx).snapshot();
4782 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
4783 let prepare_rename = project.update(cx, |project, cx| {
4784 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
4785 });
4786
4787 Some(cx.spawn(|this, mut cx| async move {
4788 let rename_range = if let Some(range) = prepare_rename.await? {
4789 Some(range)
4790 } else {
4791 this.read_with(&cx, |this, cx| {
4792 let buffer = this.buffer.read(cx).snapshot(cx);
4793 let mut buffer_highlights = this
4794 .document_highlights_for_position(selection.head(), &buffer)
4795 .filter(|highlight| {
4796 highlight.start.excerpt_id() == selection.head().excerpt_id()
4797 && highlight.end.excerpt_id() == selection.head().excerpt_id()
4798 });
4799 buffer_highlights
4800 .next()
4801 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
4802 })
4803 };
4804 if let Some(rename_range) = rename_range {
4805 let rename_buffer_range = rename_range.to_offset(&snapshot);
4806 let cursor_offset_in_rename_range =
4807 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
4808
4809 this.update(&mut cx, |this, cx| {
4810 this.take_rename(false, cx);
4811 let style = this.style(cx);
4812 let buffer = this.buffer.read(cx).read(cx);
4813 let cursor_offset = selection.head().to_offset(&buffer);
4814 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
4815 let rename_end = rename_start + rename_buffer_range.len();
4816 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
4817 let mut old_highlight_id = None;
4818 let old_name: Arc<str> = buffer
4819 .chunks(rename_start..rename_end, true)
4820 .map(|chunk| {
4821 if old_highlight_id.is_none() {
4822 old_highlight_id = chunk.syntax_highlight_id;
4823 }
4824 chunk.text
4825 })
4826 .collect::<String>()
4827 .into();
4828
4829 drop(buffer);
4830
4831 // Position the selection in the rename editor so that it matches the current selection.
4832 this.show_local_selections = false;
4833 let rename_editor = cx.add_view(|cx| {
4834 println!("Rename editor created.");
4835 let mut editor = Editor::single_line(None, cx);
4836 if let Some(old_highlight_id) = old_highlight_id {
4837 editor.override_text_style =
4838 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
4839 }
4840 editor
4841 .buffer
4842 .update(cx, |buffer, cx| buffer.edit([(0..0, old_name.clone())], cx));
4843 editor.select_all(&SelectAll, cx);
4844 editor
4845 });
4846
4847 let ranges = this
4848 .clear_background_highlights::<DocumentHighlightWrite>(cx)
4849 .into_iter()
4850 .flat_map(|(_, ranges)| ranges)
4851 .chain(
4852 this.clear_background_highlights::<DocumentHighlightRead>(cx)
4853 .into_iter()
4854 .flat_map(|(_, ranges)| ranges),
4855 )
4856 .collect();
4857
4858 this.highlight_text::<Rename>(
4859 ranges,
4860 HighlightStyle {
4861 fade_out: Some(style.rename_fade),
4862 ..Default::default()
4863 },
4864 cx,
4865 );
4866 cx.focus(&rename_editor);
4867 let block_id = this.insert_blocks(
4868 [BlockProperties {
4869 position: range.start.clone(),
4870 height: 1,
4871 render: Arc::new({
4872 let editor = rename_editor.clone();
4873 move |cx: &mut BlockContext| {
4874 ChildView::new(editor.clone())
4875 .contained()
4876 .with_padding_left(cx.anchor_x)
4877 .boxed()
4878 }
4879 }),
4880 disposition: BlockDisposition::Below,
4881 }],
4882 cx,
4883 )[0];
4884 this.pending_rename = Some(RenameState {
4885 range,
4886 old_name,
4887 editor: rename_editor,
4888 block_id,
4889 });
4890 });
4891 }
4892
4893 Ok(())
4894 }))
4895 }
4896
4897 pub fn confirm_rename(
4898 workspace: &mut Workspace,
4899 _: &ConfirmRename,
4900 cx: &mut ViewContext<Workspace>,
4901 ) -> Option<Task<Result<()>>> {
4902 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
4903
4904 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
4905 let rename = editor.take_rename(false, cx)?;
4906 let buffer = editor.buffer.read(cx);
4907 let (start_buffer, start) =
4908 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
4909 let (end_buffer, end) =
4910 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
4911 if start_buffer == end_buffer {
4912 let new_name = rename.editor.read(cx).text(cx);
4913 Some((start_buffer, start..end, rename.old_name, new_name))
4914 } else {
4915 None
4916 }
4917 })?;
4918
4919 let rename = workspace.project().clone().update(cx, |project, cx| {
4920 project.perform_rename(
4921 buffer.clone(),
4922 range.start.clone(),
4923 new_name.clone(),
4924 true,
4925 cx,
4926 )
4927 });
4928
4929 Some(cx.spawn(|workspace, mut cx| async move {
4930 let project_transaction = rename.await?;
4931 Self::open_project_transaction(
4932 editor.clone(),
4933 workspace,
4934 project_transaction,
4935 format!("Rename: {} → {}", old_name, new_name),
4936 cx.clone(),
4937 )
4938 .await?;
4939
4940 editor.update(&mut cx, |editor, cx| {
4941 editor.refresh_document_highlights(cx);
4942 });
4943 Ok(())
4944 }))
4945 }
4946
4947 fn take_rename(
4948 &mut self,
4949 moving_cursor: bool,
4950 cx: &mut ViewContext<Self>,
4951 ) -> Option<RenameState> {
4952 let rename = self.pending_rename.take()?;
4953 self.remove_blocks([rename.block_id].into_iter().collect(), cx);
4954 self.clear_text_highlights::<Rename>(cx);
4955 self.show_local_selections = true;
4956
4957 if moving_cursor {
4958 let rename_editor = rename.editor.read(cx);
4959 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
4960
4961 // Update the selection to match the position of the selection inside
4962 // the rename editor.
4963 let snapshot = self.buffer.read(cx).read(cx);
4964 let rename_range = rename.range.to_offset(&snapshot);
4965 let cursor_in_editor = snapshot
4966 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
4967 .min(rename_range.end);
4968 drop(snapshot);
4969
4970 self.change_selections(None, cx, |s| {
4971 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
4972 });
4973 }
4974
4975 Some(rename)
4976 }
4977
4978 #[cfg(any(test, feature = "test-support"))]
4979 pub fn pending_rename(&self) -> Option<&RenameState> {
4980 self.pending_rename.as_ref()
4981 }
4982
4983 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
4984 if let Some(project) = self.project.clone() {
4985 self.buffer.update(cx, |multi_buffer, cx| {
4986 project.update(cx, |project, cx| {
4987 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
4988 });
4989 })
4990 }
4991 }
4992
4993 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
4994 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
4995 let buffer = self.buffer.read(cx).snapshot(cx);
4996 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
4997 let is_valid = buffer
4998 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
4999 .any(|entry| {
5000 entry.diagnostic.is_primary
5001 && !entry.range.is_empty()
5002 && entry.range.start == primary_range_start
5003 && entry.diagnostic.message == active_diagnostics.primary_message
5004 });
5005
5006 if is_valid != active_diagnostics.is_valid {
5007 active_diagnostics.is_valid = is_valid;
5008 let mut new_styles = HashMap::default();
5009 for (block_id, diagnostic) in &active_diagnostics.blocks {
5010 new_styles.insert(
5011 *block_id,
5012 diagnostic_block_renderer(diagnostic.clone(), is_valid),
5013 );
5014 }
5015 self.display_map
5016 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
5017 }
5018 }
5019 }
5020
5021 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) {
5022 self.dismiss_diagnostics(cx);
5023 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
5024 let buffer = self.buffer.read(cx).snapshot(cx);
5025
5026 let mut primary_range = None;
5027 let mut primary_message = None;
5028 let mut group_end = Point::zero();
5029 let diagnostic_group = buffer
5030 .diagnostic_group::<Point>(group_id)
5031 .map(|entry| {
5032 if entry.range.end > group_end {
5033 group_end = entry.range.end;
5034 }
5035 if entry.diagnostic.is_primary {
5036 primary_range = Some(entry.range.clone());
5037 primary_message = Some(entry.diagnostic.message.clone());
5038 }
5039 entry
5040 })
5041 .collect::<Vec<_>>();
5042 let primary_range = primary_range.unwrap();
5043 let primary_message = primary_message.unwrap();
5044 let primary_range =
5045 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
5046
5047 let blocks = display_map
5048 .insert_blocks(
5049 diagnostic_group.iter().map(|entry| {
5050 let diagnostic = entry.diagnostic.clone();
5051 let message_height = diagnostic.message.lines().count() as u8;
5052 BlockProperties {
5053 position: buffer.anchor_after(entry.range.start),
5054 height: message_height,
5055 render: diagnostic_block_renderer(diagnostic, true),
5056 disposition: BlockDisposition::Below,
5057 }
5058 }),
5059 cx,
5060 )
5061 .into_iter()
5062 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
5063 .collect();
5064
5065 Some(ActiveDiagnosticGroup {
5066 primary_range,
5067 primary_message,
5068 blocks,
5069 is_valid: true,
5070 })
5071 });
5072 }
5073
5074 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
5075 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
5076 self.display_map.update(cx, |display_map, cx| {
5077 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
5078 });
5079 cx.notify();
5080 }
5081 }
5082
5083 pub fn set_selections_from_remote(
5084 &mut self,
5085 selections: Vec<Selection<Anchor>>,
5086 cx: &mut ViewContext<Self>,
5087 ) {
5088 let old_cursor_position = self.selections.newest_anchor().head();
5089 self.selections.change_with(cx, |s| {
5090 s.select_anchors(selections);
5091 });
5092 self.selections_did_change(false, &old_cursor_position, cx);
5093 }
5094
5095 fn push_to_selection_history(&mut self) {
5096 self.selection_history.push(SelectionHistoryEntry {
5097 selections: self.selections.disjoint_anchors().clone(),
5098 select_next_state: self.select_next_state.clone(),
5099 add_selections_state: self.add_selections_state.clone(),
5100 });
5101 }
5102
5103 pub fn request_autoscroll(&mut self, autoscroll: Autoscroll, cx: &mut ViewContext<Self>) {
5104 self.autoscroll_request = Some((autoscroll, true));
5105 cx.notify();
5106 }
5107
5108 fn request_autoscroll_remotely(&mut self, autoscroll: Autoscroll, cx: &mut ViewContext<Self>) {
5109 self.autoscroll_request = Some((autoscroll, false));
5110 cx.notify();
5111 }
5112
5113 pub fn transact(
5114 &mut self,
5115 cx: &mut ViewContext<Self>,
5116 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
5117 ) {
5118 self.start_transaction_at(Instant::now(), cx);
5119 update(self, cx);
5120 self.end_transaction_at(Instant::now(), cx);
5121 }
5122
5123 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
5124 self.end_selection(cx);
5125 if let Some(tx_id) = self
5126 .buffer
5127 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
5128 {
5129 self.selection_history
5130 .insert_transaction(tx_id, self.selections.disjoint_anchors().clone());
5131 }
5132 }
5133
5134 fn end_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
5135 if let Some(tx_id) = self
5136 .buffer
5137 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
5138 {
5139 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
5140 *end_selections = Some(self.selections.disjoint_anchors().clone());
5141 } else {
5142 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
5143 }
5144
5145 cx.emit(Event::Edited);
5146 }
5147 }
5148
5149 pub fn page_up(&mut self, _: &PageUp, _: &mut ViewContext<Self>) {
5150 log::info!("Editor::page_up");
5151 }
5152
5153 pub fn page_down(&mut self, _: &PageDown, _: &mut ViewContext<Self>) {
5154 log::info!("Editor::page_down");
5155 }
5156
5157 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
5158 let mut fold_ranges = Vec::new();
5159
5160 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5161 let selections = self.selections.all::<Point>(cx);
5162 for selection in selections {
5163 let range = selection.display_range(&display_map).sorted();
5164 let buffer_start_row = range.start.to_point(&display_map).row;
5165
5166 for row in (0..=range.end.row()).rev() {
5167 if self.is_line_foldable(&display_map, row) && !display_map.is_line_folded(row) {
5168 let fold_range = self.foldable_range_for_line(&display_map, row);
5169 if fold_range.end.row >= buffer_start_row {
5170 fold_ranges.push(fold_range);
5171 if row <= range.start.row() {
5172 break;
5173 }
5174 }
5175 }
5176 }
5177 }
5178
5179 self.fold_ranges(fold_ranges, cx);
5180 }
5181
5182 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
5183 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5184 let buffer = &display_map.buffer_snapshot;
5185 let selections = self.selections.all::<Point>(cx);
5186 let ranges = selections
5187 .iter()
5188 .map(|s| {
5189 let range = s.display_range(&display_map).sorted();
5190 let mut start = range.start.to_point(&display_map);
5191 let mut end = range.end.to_point(&display_map);
5192 start.column = 0;
5193 end.column = buffer.line_len(end.row);
5194 start..end
5195 })
5196 .collect::<Vec<_>>();
5197 self.unfold_ranges(ranges, true, cx);
5198 }
5199
5200 fn is_line_foldable(&self, display_map: &DisplaySnapshot, display_row: u32) -> bool {
5201 let max_point = display_map.max_point();
5202 if display_row >= max_point.row() {
5203 false
5204 } else {
5205 let (start_indent, is_blank) = display_map.line_indent(display_row);
5206 if is_blank {
5207 false
5208 } else {
5209 for display_row in display_row + 1..=max_point.row() {
5210 let (indent, is_blank) = display_map.line_indent(display_row);
5211 if !is_blank {
5212 return indent > start_indent;
5213 }
5214 }
5215 false
5216 }
5217 }
5218 }
5219
5220 fn foldable_range_for_line(
5221 &self,
5222 display_map: &DisplaySnapshot,
5223 start_row: u32,
5224 ) -> Range<Point> {
5225 let max_point = display_map.max_point();
5226
5227 let (start_indent, _) = display_map.line_indent(start_row);
5228 let start = DisplayPoint::new(start_row, display_map.line_len(start_row));
5229 let mut end = None;
5230 for row in start_row + 1..=max_point.row() {
5231 let (indent, is_blank) = display_map.line_indent(row);
5232 if !is_blank && indent <= start_indent {
5233 end = Some(DisplayPoint::new(row - 1, display_map.line_len(row - 1)));
5234 break;
5235 }
5236 }
5237
5238 let end = end.unwrap_or(max_point);
5239 return start.to_point(display_map)..end.to_point(display_map);
5240 }
5241
5242 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
5243 let selections = self.selections.all::<Point>(cx);
5244 let ranges = selections.into_iter().map(|s| s.start..s.end);
5245 self.fold_ranges(ranges, cx);
5246 }
5247
5248 pub fn fold_ranges<T: ToOffset>(
5249 &mut self,
5250 ranges: impl IntoIterator<Item = Range<T>>,
5251 cx: &mut ViewContext<Self>,
5252 ) {
5253 let mut ranges = ranges.into_iter().peekable();
5254 if ranges.peek().is_some() {
5255 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
5256 self.request_autoscroll(Autoscroll::Fit, cx);
5257 cx.notify();
5258 }
5259 }
5260
5261 pub fn unfold_ranges<T: ToOffset>(
5262 &mut self,
5263 ranges: impl IntoIterator<Item = Range<T>>,
5264 inclusive: bool,
5265 cx: &mut ViewContext<Self>,
5266 ) {
5267 let mut ranges = ranges.into_iter().peekable();
5268 if ranges.peek().is_some() {
5269 self.display_map
5270 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
5271 self.request_autoscroll(Autoscroll::Fit, cx);
5272 cx.notify();
5273 }
5274 }
5275
5276 pub fn insert_blocks(
5277 &mut self,
5278 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
5279 cx: &mut ViewContext<Self>,
5280 ) -> Vec<BlockId> {
5281 let blocks = self
5282 .display_map
5283 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
5284 self.request_autoscroll(Autoscroll::Fit, cx);
5285 blocks
5286 }
5287
5288 pub fn replace_blocks(
5289 &mut self,
5290 blocks: HashMap<BlockId, RenderBlock>,
5291 cx: &mut ViewContext<Self>,
5292 ) {
5293 self.display_map
5294 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
5295 self.request_autoscroll(Autoscroll::Fit, cx);
5296 }
5297
5298 pub fn remove_blocks(&mut self, block_ids: HashSet<BlockId>, cx: &mut ViewContext<Self>) {
5299 self.display_map.update(cx, |display_map, cx| {
5300 display_map.remove_blocks(block_ids, cx)
5301 });
5302 }
5303
5304 pub fn longest_row(&self, cx: &mut MutableAppContext) -> u32 {
5305 self.display_map
5306 .update(cx, |map, cx| map.snapshot(cx))
5307 .longest_row()
5308 }
5309
5310 pub fn max_point(&self, cx: &mut MutableAppContext) -> DisplayPoint {
5311 self.display_map
5312 .update(cx, |map, cx| map.snapshot(cx))
5313 .max_point()
5314 }
5315
5316 pub fn text(&self, cx: &AppContext) -> String {
5317 self.buffer.read(cx).read(cx).text()
5318 }
5319
5320 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
5321 self.transact(cx, |this, cx| {
5322 this.buffer
5323 .read(cx)
5324 .as_singleton()
5325 .expect("you can only call set_text on editors for singleton buffers")
5326 .update(cx, |buffer, cx| buffer.set_text(text, cx));
5327 });
5328 }
5329
5330 pub fn display_text(&self, cx: &mut MutableAppContext) -> String {
5331 self.display_map
5332 .update(cx, |map, cx| map.snapshot(cx))
5333 .text()
5334 }
5335
5336 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
5337 let language_name = self
5338 .buffer
5339 .read(cx)
5340 .as_singleton()
5341 .and_then(|singleton_buffer| singleton_buffer.read(cx).language())
5342 .map(|l| l.name());
5343
5344 let settings = cx.global::<Settings>();
5345 let mode = self
5346 .soft_wrap_mode_override
5347 .unwrap_or_else(|| settings.soft_wrap(language_name.as_deref()));
5348 match mode {
5349 settings::SoftWrap::None => SoftWrap::None,
5350 settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
5351 settings::SoftWrap::PreferredLineLength => {
5352 SoftWrap::Column(settings.preferred_line_length(language_name.as_deref()))
5353 }
5354 }
5355 }
5356
5357 pub fn set_soft_wrap_mode(&mut self, mode: settings::SoftWrap, cx: &mut ViewContext<Self>) {
5358 self.soft_wrap_mode_override = Some(mode);
5359 cx.notify();
5360 }
5361
5362 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut MutableAppContext) -> bool {
5363 self.display_map
5364 .update(cx, |map, cx| map.set_wrap_width(width, cx))
5365 }
5366
5367 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
5368 self.highlighted_rows = rows;
5369 }
5370
5371 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
5372 self.highlighted_rows.clone()
5373 }
5374
5375 pub fn highlight_background<T: 'static>(
5376 &mut self,
5377 ranges: Vec<Range<Anchor>>,
5378 color_fetcher: fn(&Theme) -> Color,
5379 cx: &mut ViewContext<Self>,
5380 ) {
5381 self.background_highlights
5382 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
5383 cx.notify();
5384 }
5385
5386 pub fn clear_background_highlights<T: 'static>(
5387 &mut self,
5388 cx: &mut ViewContext<Self>,
5389 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
5390 cx.notify();
5391 self.background_highlights.remove(&TypeId::of::<T>())
5392 }
5393
5394 #[cfg(feature = "test-support")]
5395 pub fn all_background_highlights(
5396 &mut self,
5397 cx: &mut ViewContext<Self>,
5398 ) -> Vec<(Range<DisplayPoint>, Color)> {
5399 let snapshot = self.snapshot(cx);
5400 let buffer = &snapshot.buffer_snapshot;
5401 let start = buffer.anchor_before(0);
5402 let end = buffer.anchor_after(buffer.len());
5403 let theme = cx.global::<Settings>().theme.as_ref();
5404 self.background_highlights_in_range(start..end, &snapshot, theme)
5405 }
5406
5407 fn document_highlights_for_position<'a>(
5408 &'a self,
5409 position: Anchor,
5410 buffer: &'a MultiBufferSnapshot,
5411 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
5412 let read_highlights = self
5413 .background_highlights
5414 .get(&TypeId::of::<DocumentHighlightRead>())
5415 .map(|h| &h.1);
5416 let write_highlights = self
5417 .background_highlights
5418 .get(&TypeId::of::<DocumentHighlightRead>())
5419 .map(|h| &h.1);
5420 let left_position = position.bias_left(buffer);
5421 let right_position = position.bias_right(buffer);
5422 read_highlights
5423 .into_iter()
5424 .chain(write_highlights)
5425 .flat_map(move |ranges| {
5426 let start_ix = match ranges.binary_search_by(|probe| {
5427 let cmp = probe.end.cmp(&left_position, &buffer);
5428 if cmp.is_ge() {
5429 Ordering::Greater
5430 } else {
5431 Ordering::Less
5432 }
5433 }) {
5434 Ok(i) | Err(i) => i,
5435 };
5436
5437 let right_position = right_position.clone();
5438 ranges[start_ix..]
5439 .iter()
5440 .take_while(move |range| range.start.cmp(&right_position, &buffer).is_le())
5441 })
5442 }
5443
5444 pub fn background_highlights_in_range(
5445 &self,
5446 search_range: Range<Anchor>,
5447 display_snapshot: &DisplaySnapshot,
5448 theme: &Theme,
5449 ) -> Vec<(Range<DisplayPoint>, Color)> {
5450 let mut results = Vec::new();
5451 let buffer = &display_snapshot.buffer_snapshot;
5452 for (color_fetcher, ranges) in self.background_highlights.values() {
5453 let color = color_fetcher(theme);
5454 let start_ix = match ranges.binary_search_by(|probe| {
5455 let cmp = probe.end.cmp(&search_range.start, &buffer);
5456 if cmp.is_gt() {
5457 Ordering::Greater
5458 } else {
5459 Ordering::Less
5460 }
5461 }) {
5462 Ok(i) | Err(i) => i,
5463 };
5464 for range in &ranges[start_ix..] {
5465 if range.start.cmp(&search_range.end, &buffer).is_ge() {
5466 break;
5467 }
5468 let start = range
5469 .start
5470 .to_point(buffer)
5471 .to_display_point(display_snapshot);
5472 let end = range
5473 .end
5474 .to_point(buffer)
5475 .to_display_point(display_snapshot);
5476 results.push((start..end, color))
5477 }
5478 }
5479 results
5480 }
5481
5482 pub fn highlight_text<T: 'static>(
5483 &mut self,
5484 ranges: Vec<Range<Anchor>>,
5485 style: HighlightStyle,
5486 cx: &mut ViewContext<Self>,
5487 ) {
5488 self.display_map.update(cx, |map, _| {
5489 map.highlight_text(TypeId::of::<T>(), ranges, style)
5490 });
5491 cx.notify();
5492 }
5493
5494 pub fn clear_text_highlights<T: 'static>(
5495 &mut self,
5496 cx: &mut ViewContext<Self>,
5497 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
5498 cx.notify();
5499 self.display_map
5500 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()))
5501 }
5502
5503 fn next_blink_epoch(&mut self) -> usize {
5504 self.blink_epoch += 1;
5505 self.blink_epoch
5506 }
5507
5508 fn pause_cursor_blinking(&mut self, cx: &mut ViewContext<Self>) {
5509 if !self.focused {
5510 return;
5511 }
5512
5513 self.show_local_cursors = true;
5514 cx.notify();
5515
5516 let epoch = self.next_blink_epoch();
5517 cx.spawn(|this, mut cx| {
5518 let this = this.downgrade();
5519 async move {
5520 Timer::after(CURSOR_BLINK_INTERVAL).await;
5521 if let Some(this) = this.upgrade(&cx) {
5522 this.update(&mut cx, |this, cx| this.resume_cursor_blinking(epoch, cx))
5523 }
5524 }
5525 })
5526 .detach();
5527 }
5528
5529 fn resume_cursor_blinking(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
5530 if epoch == self.blink_epoch {
5531 self.blinking_paused = false;
5532 self.blink_cursors(epoch, cx);
5533 }
5534 }
5535
5536 fn blink_cursors(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
5537 if epoch == self.blink_epoch && self.focused && !self.blinking_paused {
5538 self.show_local_cursors = !self.show_local_cursors;
5539 cx.notify();
5540
5541 let epoch = self.next_blink_epoch();
5542 cx.spawn(|this, mut cx| {
5543 let this = this.downgrade();
5544 async move {
5545 Timer::after(CURSOR_BLINK_INTERVAL).await;
5546 if let Some(this) = this.upgrade(&cx) {
5547 this.update(&mut cx, |this, cx| this.blink_cursors(epoch, cx));
5548 }
5549 }
5550 })
5551 .detach();
5552 }
5553 }
5554
5555 pub fn show_local_cursors(&self) -> bool {
5556 self.show_local_cursors && self.focused
5557 }
5558
5559 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
5560 cx.notify();
5561 }
5562
5563 fn on_buffer_event(
5564 &mut self,
5565 _: ModelHandle<MultiBuffer>,
5566 event: &language::Event,
5567 cx: &mut ViewContext<Self>,
5568 ) {
5569 match event {
5570 language::Event::Edited => {
5571 self.refresh_active_diagnostics(cx);
5572 self.refresh_code_actions(cx);
5573 cx.emit(Event::BufferEdited);
5574 }
5575 language::Event::Reparsed => cx.emit(Event::Reparsed),
5576 language::Event::Dirtied => cx.emit(Event::Dirtied),
5577 language::Event::Saved => cx.emit(Event::Saved),
5578 language::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
5579 language::Event::Reloaded => cx.emit(Event::TitleChanged),
5580 language::Event::Closed => cx.emit(Event::Closed),
5581 language::Event::DiagnosticsUpdated => {
5582 self.refresh_active_diagnostics(cx);
5583 }
5584 _ => {}
5585 }
5586 }
5587
5588 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
5589 cx.notify();
5590 }
5591
5592 pub fn set_searchable(&mut self, searchable: bool) {
5593 self.searchable = searchable;
5594 }
5595
5596 pub fn searchable(&self) -> bool {
5597 self.searchable
5598 }
5599
5600 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
5601 let active_item = workspace.active_item(cx);
5602 let editor_handle = if let Some(editor) = active_item
5603 .as_ref()
5604 .and_then(|item| item.act_as::<Self>(cx))
5605 {
5606 editor
5607 } else {
5608 cx.propagate_action();
5609 return;
5610 };
5611
5612 let editor = editor_handle.read(cx);
5613 let buffer = editor.buffer.read(cx);
5614 if buffer.is_singleton() {
5615 cx.propagate_action();
5616 return;
5617 }
5618
5619 let mut new_selections_by_buffer = HashMap::default();
5620 for selection in editor.selections.all::<usize>(cx) {
5621 for (buffer, mut range) in
5622 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
5623 {
5624 if selection.reversed {
5625 mem::swap(&mut range.start, &mut range.end);
5626 }
5627 new_selections_by_buffer
5628 .entry(buffer)
5629 .or_insert(Vec::new())
5630 .push(range)
5631 }
5632 }
5633
5634 editor_handle.update(cx, |editor, cx| {
5635 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
5636 });
5637 let nav_history = workspace.active_pane().read(cx).nav_history().clone();
5638 nav_history.borrow_mut().disable();
5639
5640 // We defer the pane interaction because we ourselves are a workspace item
5641 // and activating a new item causes the pane to call a method on us reentrantly,
5642 // which panics if we're on the stack.
5643 cx.defer(move |workspace, cx| {
5644 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
5645 let editor = workspace.open_project_item::<Self>(buffer, cx);
5646 editor.update(cx, |editor, cx| {
5647 editor.change_selections(Some(Autoscroll::Newest), cx, |s| {
5648 s.select_ranges(ranges);
5649 });
5650 });
5651 }
5652
5653 nav_history.borrow_mut().enable();
5654 });
5655 }
5656}
5657
5658impl EditorSnapshot {
5659 pub fn is_focused(&self) -> bool {
5660 self.is_focused
5661 }
5662
5663 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
5664 self.placeholder_text.as_ref()
5665 }
5666
5667 pub fn scroll_position(&self) -> Vector2F {
5668 compute_scroll_position(
5669 &self.display_snapshot,
5670 self.scroll_position,
5671 &self.scroll_top_anchor,
5672 )
5673 }
5674}
5675
5676impl Deref for EditorSnapshot {
5677 type Target = DisplaySnapshot;
5678
5679 fn deref(&self) -> &Self::Target {
5680 &self.display_snapshot
5681 }
5682}
5683
5684fn compute_scroll_position(
5685 snapshot: &DisplaySnapshot,
5686 mut scroll_position: Vector2F,
5687 scroll_top_anchor: &Anchor,
5688) -> Vector2F {
5689 if *scroll_top_anchor != Anchor::min() {
5690 let scroll_top = scroll_top_anchor.to_display_point(snapshot).row() as f32;
5691 scroll_position.set_y(scroll_top + scroll_position.y());
5692 } else {
5693 scroll_position.set_y(0.);
5694 }
5695 scroll_position
5696}
5697
5698#[derive(Copy, Clone, Debug, PartialEq, Eq)]
5699pub enum Event {
5700 Activate,
5701 BufferEdited,
5702 Edited,
5703 Reparsed,
5704 Blurred,
5705 Dirtied,
5706 Saved,
5707 TitleChanged,
5708 SelectionsChanged { local: bool },
5709 ScrollPositionChanged { local: bool },
5710 Closed,
5711}
5712
5713pub struct EditorFocused(pub ViewHandle<Editor>);
5714pub struct EditorBlurred(pub ViewHandle<Editor>);
5715pub struct EditorReleased(pub WeakViewHandle<Editor>);
5716
5717impl Entity for Editor {
5718 type Event = Event;
5719
5720 fn release(&mut self, cx: &mut MutableAppContext) {
5721 cx.emit_global(EditorReleased(self.handle.clone()));
5722 }
5723}
5724
5725impl View for Editor {
5726 fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
5727 let style = self.style(cx);
5728 self.display_map.update(cx, |map, cx| {
5729 map.set_font(style.text.font_id, style.text.font_size, cx)
5730 });
5731 EditorElement::new(self.handle.clone(), style.clone(), self.cursor_shape).boxed()
5732 }
5733
5734 fn ui_name() -> &'static str {
5735 "Editor"
5736 }
5737
5738 fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
5739 let focused_event = EditorFocused(cx.handle());
5740 cx.emit_global(focused_event);
5741 if let Some(rename) = self.pending_rename.as_ref() {
5742 cx.focus(&rename.editor);
5743 } else {
5744 self.focused = true;
5745 self.blink_cursors(self.blink_epoch, cx);
5746 self.buffer.update(cx, |buffer, cx| {
5747 buffer.finalize_last_transaction(cx);
5748 if self.leader_replica_id.is_none() {
5749 buffer.set_active_selections(
5750 &self.selections.disjoint_anchors(),
5751 self.selections.line_mode,
5752 cx,
5753 );
5754 }
5755 });
5756 }
5757 }
5758
5759 fn on_blur(&mut self, cx: &mut ViewContext<Self>) {
5760 let blurred_event = EditorBlurred(cx.handle());
5761 cx.emit_global(blurred_event);
5762 self.focused = false;
5763 self.buffer
5764 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
5765 self.hide_context_menu(cx);
5766 cx.emit(Event::Blurred);
5767 cx.notify();
5768 }
5769
5770 fn keymap_context(&self, _: &AppContext) -> gpui::keymap::Context {
5771 let mut context = Self::default_keymap_context();
5772 let mode = match self.mode {
5773 EditorMode::SingleLine => "single_line",
5774 EditorMode::AutoHeight { .. } => "auto_height",
5775 EditorMode::Full => "full",
5776 };
5777 context.map.insert("mode".into(), mode.into());
5778 if self.pending_rename.is_some() {
5779 context.set.insert("renaming".into());
5780 }
5781 match self.context_menu.as_ref() {
5782 Some(ContextMenu::Completions(_)) => {
5783 context.set.insert("showing_completions".into());
5784 }
5785 Some(ContextMenu::CodeActions(_)) => {
5786 context.set.insert("showing_code_actions".into());
5787 }
5788 Some(ContextMenu::Hover(_)) => {
5789 context.set.insert("showing_hover".into());
5790 }
5791 None => {}
5792 }
5793
5794 for layer in self.keymap_context_layers.values() {
5795 context.extend(layer);
5796 }
5797
5798 context
5799 }
5800}
5801
5802fn build_style(
5803 settings: &Settings,
5804 get_field_editor_theme: Option<GetFieldEditorTheme>,
5805 override_text_style: Option<&OverrideTextStyle>,
5806 cx: &AppContext,
5807) -> EditorStyle {
5808 let font_cache = cx.font_cache();
5809
5810 let mut theme = settings.theme.editor.clone();
5811 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
5812 let field_editor_theme = get_field_editor_theme(&settings.theme);
5813 theme.text_color = field_editor_theme.text.color;
5814 theme.selection = field_editor_theme.selection;
5815 theme.background = field_editor_theme
5816 .container
5817 .background_color
5818 .unwrap_or_default();
5819 EditorStyle {
5820 text: field_editor_theme.text,
5821 placeholder_text: field_editor_theme.placeholder_text,
5822 theme,
5823 }
5824 } else {
5825 let font_family_id = settings.buffer_font_family;
5826 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
5827 let font_properties = Default::default();
5828 let font_id = font_cache
5829 .select_font(font_family_id, &font_properties)
5830 .unwrap();
5831 let font_size = settings.buffer_font_size;
5832 EditorStyle {
5833 text: TextStyle {
5834 color: settings.theme.editor.text_color,
5835 font_family_name,
5836 font_family_id,
5837 font_id,
5838 font_size,
5839 font_properties,
5840 underline: Default::default(),
5841 },
5842 placeholder_text: None,
5843 theme,
5844 }
5845 };
5846
5847 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
5848 if let Some(highlighted) = style
5849 .text
5850 .clone()
5851 .highlight(highlight_style, font_cache)
5852 .log_err()
5853 {
5854 style.text = highlighted;
5855 }
5856 }
5857
5858 style
5859}
5860
5861trait SelectionExt {
5862 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
5863 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
5864 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
5865 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
5866 -> Range<u32>;
5867}
5868
5869impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
5870 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
5871 let start = self.start.to_point(buffer);
5872 let end = self.end.to_point(buffer);
5873 if self.reversed {
5874 end..start
5875 } else {
5876 start..end
5877 }
5878 }
5879
5880 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
5881 let start = self.start.to_offset(buffer);
5882 let end = self.end.to_offset(buffer);
5883 if self.reversed {
5884 end..start
5885 } else {
5886 start..end
5887 }
5888 }
5889
5890 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
5891 let start = self
5892 .start
5893 .to_point(&map.buffer_snapshot)
5894 .to_display_point(map);
5895 let end = self
5896 .end
5897 .to_point(&map.buffer_snapshot)
5898 .to_display_point(map);
5899 if self.reversed {
5900 end..start
5901 } else {
5902 start..end
5903 }
5904 }
5905
5906 fn spanned_rows(
5907 &self,
5908 include_end_if_at_line_start: bool,
5909 map: &DisplaySnapshot,
5910 ) -> Range<u32> {
5911 let start = self.start.to_point(&map.buffer_snapshot);
5912 let mut end = self.end.to_point(&map.buffer_snapshot);
5913 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
5914 end.row -= 1;
5915 }
5916
5917 let buffer_start = map.prev_line_boundary(start).0;
5918 let buffer_end = map.next_line_boundary(end).0;
5919 buffer_start.row..buffer_end.row + 1
5920 }
5921}
5922
5923impl<T: InvalidationRegion> InvalidationStack<T> {
5924 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
5925 where
5926 S: Clone + ToOffset,
5927 {
5928 while let Some(region) = self.last() {
5929 let all_selections_inside_invalidation_ranges =
5930 if selections.len() == region.ranges().len() {
5931 selections
5932 .iter()
5933 .zip(region.ranges().iter().map(|r| r.to_offset(&buffer)))
5934 .all(|(selection, invalidation_range)| {
5935 let head = selection.head().to_offset(&buffer);
5936 invalidation_range.start <= head && invalidation_range.end >= head
5937 })
5938 } else {
5939 false
5940 };
5941
5942 if all_selections_inside_invalidation_ranges {
5943 break;
5944 } else {
5945 self.pop();
5946 }
5947 }
5948 }
5949}
5950
5951impl<T> Default for InvalidationStack<T> {
5952 fn default() -> Self {
5953 Self(Default::default())
5954 }
5955}
5956
5957impl<T> Deref for InvalidationStack<T> {
5958 type Target = Vec<T>;
5959
5960 fn deref(&self) -> &Self::Target {
5961 &self.0
5962 }
5963}
5964
5965impl<T> DerefMut for InvalidationStack<T> {
5966 fn deref_mut(&mut self) -> &mut Self::Target {
5967 &mut self.0
5968 }
5969}
5970
5971impl InvalidationRegion for BracketPairState {
5972 fn ranges(&self) -> &[Range<Anchor>] {
5973 &self.ranges
5974 }
5975}
5976
5977impl InvalidationRegion for SnippetState {
5978 fn ranges(&self) -> &[Range<Anchor>] {
5979 &self.ranges[self.active_index]
5980 }
5981}
5982
5983impl Deref for EditorStyle {
5984 type Target = theme::Editor;
5985
5986 fn deref(&self) -> &Self::Target {
5987 &self.theme
5988 }
5989}
5990
5991pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
5992 let mut highlighted_lines = Vec::new();
5993 for line in diagnostic.message.lines() {
5994 highlighted_lines.push(highlight_diagnostic_message(line));
5995 }
5996
5997 Arc::new(move |cx: &mut BlockContext| {
5998 let settings = cx.global::<Settings>();
5999 let theme = &settings.theme.editor;
6000 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
6001 let font_size = (style.text_scale_factor * settings.buffer_font_size).round();
6002 Flex::column()
6003 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
6004 Label::new(
6005 line.clone(),
6006 style.message.clone().with_font_size(font_size),
6007 )
6008 .with_highlights(highlights.clone())
6009 .contained()
6010 .with_margin_left(cx.anchor_x)
6011 .boxed()
6012 }))
6013 .aligned()
6014 .left()
6015 .boxed()
6016 })
6017}
6018
6019pub fn highlight_diagnostic_message(message: &str) -> (String, Vec<usize>) {
6020 let mut message_without_backticks = String::new();
6021 let mut prev_offset = 0;
6022 let mut inside_block = false;
6023 let mut highlights = Vec::new();
6024 for (match_ix, (offset, _)) in message
6025 .match_indices('`')
6026 .chain([(message.len(), "")])
6027 .enumerate()
6028 {
6029 message_without_backticks.push_str(&message[prev_offset..offset]);
6030 if inside_block {
6031 highlights.extend(prev_offset - match_ix..offset - match_ix);
6032 }
6033
6034 inside_block = !inside_block;
6035 prev_offset = offset + 1;
6036 }
6037
6038 (message_without_backticks, highlights)
6039}
6040
6041pub fn diagnostic_style(
6042 severity: DiagnosticSeverity,
6043 valid: bool,
6044 theme: &theme::Editor,
6045) -> DiagnosticStyle {
6046 match (severity, valid) {
6047 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
6048 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
6049 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
6050 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
6051 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
6052 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
6053 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
6054 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
6055 _ => theme.invalid_hint_diagnostic.clone(),
6056 }
6057}
6058
6059pub fn combine_syntax_and_fuzzy_match_highlights(
6060 text: &str,
6061 default_style: HighlightStyle,
6062 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
6063 match_indices: &[usize],
6064) -> Vec<(Range<usize>, HighlightStyle)> {
6065 let mut result = Vec::new();
6066 let mut match_indices = match_indices.iter().copied().peekable();
6067
6068 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
6069 {
6070 syntax_highlight.weight = None;
6071
6072 // Add highlights for any fuzzy match characters before the next
6073 // syntax highlight range.
6074 while let Some(&match_index) = match_indices.peek() {
6075 if match_index >= range.start {
6076 break;
6077 }
6078 match_indices.next();
6079 let end_index = char_ix_after(match_index, text);
6080 let mut match_style = default_style;
6081 match_style.weight = Some(fonts::Weight::BOLD);
6082 result.push((match_index..end_index, match_style));
6083 }
6084
6085 if range.start == usize::MAX {
6086 break;
6087 }
6088
6089 // Add highlights for any fuzzy match characters within the
6090 // syntax highlight range.
6091 let mut offset = range.start;
6092 while let Some(&match_index) = match_indices.peek() {
6093 if match_index >= range.end {
6094 break;
6095 }
6096
6097 match_indices.next();
6098 if match_index > offset {
6099 result.push((offset..match_index, syntax_highlight));
6100 }
6101
6102 let mut end_index = char_ix_after(match_index, text);
6103 while let Some(&next_match_index) = match_indices.peek() {
6104 if next_match_index == end_index && next_match_index < range.end {
6105 end_index = char_ix_after(next_match_index, text);
6106 match_indices.next();
6107 } else {
6108 break;
6109 }
6110 }
6111
6112 let mut match_style = syntax_highlight;
6113 match_style.weight = Some(fonts::Weight::BOLD);
6114 result.push((match_index..end_index, match_style));
6115 offset = end_index;
6116 }
6117
6118 if offset < range.end {
6119 result.push((offset..range.end, syntax_highlight));
6120 }
6121 }
6122
6123 fn char_ix_after(ix: usize, text: &str) -> usize {
6124 ix + text[ix..].chars().next().unwrap().len_utf8()
6125 }
6126
6127 result
6128}
6129
6130pub fn styled_runs_for_code_label<'a>(
6131 label: &'a CodeLabel,
6132 syntax_theme: &'a theme::SyntaxTheme,
6133) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
6134 let fade_out = HighlightStyle {
6135 fade_out: Some(0.35),
6136 ..Default::default()
6137 };
6138
6139 let mut prev_end = label.filter_range.end;
6140 label
6141 .runs
6142 .iter()
6143 .enumerate()
6144 .flat_map(move |(ix, (range, highlight_id))| {
6145 let style = if let Some(style) = highlight_id.style(syntax_theme) {
6146 style
6147 } else {
6148 return Default::default();
6149 };
6150 let mut muted_style = style.clone();
6151 muted_style.highlight(fade_out);
6152
6153 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
6154 if range.start >= label.filter_range.end {
6155 if range.start > prev_end {
6156 runs.push((prev_end..range.start, fade_out));
6157 }
6158 runs.push((range.clone(), muted_style));
6159 } else if range.end <= label.filter_range.end {
6160 runs.push((range.clone(), style));
6161 } else {
6162 runs.push((range.start..label.filter_range.end, style));
6163 runs.push((label.filter_range.end..range.end, muted_style));
6164 }
6165 prev_end = cmp::max(prev_end, range.end);
6166
6167 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
6168 runs.push((prev_end..label.text.len(), fade_out));
6169 }
6170
6171 runs
6172 })
6173}
6174
6175#[cfg(test)]
6176mod tests {
6177 use crate::test::{
6178 assert_text_with_selections, build_editor, select_ranges, EditorTestContext,
6179 };
6180
6181 use super::*;
6182 use gpui::{
6183 geometry::rect::RectF,
6184 platform::{WindowBounds, WindowOptions},
6185 };
6186 use indoc::indoc;
6187 use language::{FakeLspAdapter, LanguageConfig};
6188 use lsp::FakeLanguageServer;
6189 use project::FakeFs;
6190 use settings::LanguageOverride;
6191 use smol::stream::StreamExt;
6192 use std::{cell::RefCell, rc::Rc, time::Instant};
6193 use text::Point;
6194 use unindent::Unindent;
6195 use util::{
6196 assert_set_eq,
6197 test::{marked_text_by, marked_text_ranges, marked_text_ranges_by, sample_text},
6198 };
6199 use workspace::{FollowableItem, ItemHandle};
6200
6201 #[gpui::test]
6202 fn test_edit_events(cx: &mut MutableAppContext) {
6203 cx.set_global(Settings::test(cx));
6204 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6205
6206 let events = Rc::new(RefCell::new(Vec::new()));
6207 let (_, editor1) = cx.add_window(Default::default(), {
6208 let events = events.clone();
6209 |cx| {
6210 cx.subscribe(&cx.handle(), move |_, _, event, _| {
6211 if matches!(event, Event::Edited | Event::BufferEdited | Event::Dirtied) {
6212 events.borrow_mut().push(("editor1", *event));
6213 }
6214 })
6215 .detach();
6216 Editor::for_buffer(buffer.clone(), None, cx)
6217 }
6218 });
6219 let (_, editor2) = cx.add_window(Default::default(), {
6220 let events = events.clone();
6221 |cx| {
6222 cx.subscribe(&cx.handle(), move |_, _, event, _| {
6223 if matches!(event, Event::Edited | Event::BufferEdited | Event::Dirtied) {
6224 events.borrow_mut().push(("editor2", *event));
6225 }
6226 })
6227 .detach();
6228 Editor::for_buffer(buffer.clone(), None, cx)
6229 }
6230 });
6231 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6232
6233 // Mutating editor 1 will emit an `Edited` event only for that editor.
6234 editor1.update(cx, |editor, cx| editor.insert("X", cx));
6235 assert_eq!(
6236 mem::take(&mut *events.borrow_mut()),
6237 [
6238 ("editor1", Event::Edited),
6239 ("editor1", Event::BufferEdited),
6240 ("editor2", Event::BufferEdited),
6241 ("editor1", Event::Dirtied),
6242 ("editor2", Event::Dirtied)
6243 ]
6244 );
6245
6246 // Mutating editor 2 will emit an `Edited` event only for that editor.
6247 editor2.update(cx, |editor, cx| editor.delete(&Delete, 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 // Undoing on editor 1 will emit an `Edited` event only for that editor.
6258 editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
6259 assert_eq!(
6260 mem::take(&mut *events.borrow_mut()),
6261 [
6262 ("editor1", Event::Edited),
6263 ("editor1", Event::BufferEdited),
6264 ("editor2", Event::BufferEdited),
6265 ]
6266 );
6267
6268 // Redoing on editor 1 will emit an `Edited` event only for that editor.
6269 editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
6270 assert_eq!(
6271 mem::take(&mut *events.borrow_mut()),
6272 [
6273 ("editor1", Event::Edited),
6274 ("editor1", Event::BufferEdited),
6275 ("editor2", Event::BufferEdited),
6276 ]
6277 );
6278
6279 // Undoing on editor 2 will emit an `Edited` event only for that editor.
6280 editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
6281 assert_eq!(
6282 mem::take(&mut *events.borrow_mut()),
6283 [
6284 ("editor2", Event::Edited),
6285 ("editor1", Event::BufferEdited),
6286 ("editor2", Event::BufferEdited),
6287 ]
6288 );
6289
6290 // Redoing on editor 2 will emit an `Edited` event only for that editor.
6291 editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
6292 assert_eq!(
6293 mem::take(&mut *events.borrow_mut()),
6294 [
6295 ("editor2", Event::Edited),
6296 ("editor1", Event::BufferEdited),
6297 ("editor2", Event::BufferEdited),
6298 ]
6299 );
6300
6301 // No event is emitted when the mutation is a no-op.
6302 editor2.update(cx, |editor, cx| {
6303 editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
6304
6305 editor.backspace(&Backspace, cx);
6306 });
6307 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6308 }
6309
6310 #[gpui::test]
6311 fn test_undo_redo_with_selection_restoration(cx: &mut MutableAppContext) {
6312 cx.set_global(Settings::test(cx));
6313 let mut now = Instant::now();
6314 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6315 let group_interval = buffer.read(cx).transaction_group_interval();
6316 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
6317 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6318
6319 editor.update(cx, |editor, cx| {
6320 editor.start_transaction_at(now, cx);
6321 editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
6322
6323 editor.insert("cd", cx);
6324 editor.end_transaction_at(now, cx);
6325 assert_eq!(editor.text(cx), "12cd56");
6326 assert_eq!(editor.selections.ranges(cx), vec![4..4]);
6327
6328 editor.start_transaction_at(now, cx);
6329 editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
6330 editor.insert("e", cx);
6331 editor.end_transaction_at(now, cx);
6332 assert_eq!(editor.text(cx), "12cde6");
6333 assert_eq!(editor.selections.ranges(cx), vec![5..5]);
6334
6335 now += group_interval + Duration::from_millis(1);
6336 editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
6337
6338 // Simulate an edit in another editor
6339 buffer.update(cx, |buffer, cx| {
6340 buffer.start_transaction_at(now, cx);
6341 buffer.edit([(0..1, "a")], cx);
6342 buffer.edit([(1..1, "b")], cx);
6343 buffer.end_transaction_at(now, cx);
6344 });
6345
6346 assert_eq!(editor.text(cx), "ab2cde6");
6347 assert_eq!(editor.selections.ranges(cx), vec![3..3]);
6348
6349 // Last transaction happened past the group interval in a different editor.
6350 // Undo it individually and don't restore selections.
6351 editor.undo(&Undo, cx);
6352 assert_eq!(editor.text(cx), "12cde6");
6353 assert_eq!(editor.selections.ranges(cx), vec![2..2]);
6354
6355 // First two transactions happened within the group interval in this editor.
6356 // Undo them together and restore selections.
6357 editor.undo(&Undo, cx);
6358 editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
6359 assert_eq!(editor.text(cx), "123456");
6360 assert_eq!(editor.selections.ranges(cx), vec![0..0]);
6361
6362 // Redo the first two transactions together.
6363 editor.redo(&Redo, cx);
6364 assert_eq!(editor.text(cx), "12cde6");
6365 assert_eq!(editor.selections.ranges(cx), vec![5..5]);
6366
6367 // Redo the last transaction on its own.
6368 editor.redo(&Redo, cx);
6369 assert_eq!(editor.text(cx), "ab2cde6");
6370 assert_eq!(editor.selections.ranges(cx), vec![6..6]);
6371
6372 // Test empty transactions.
6373 editor.start_transaction_at(now, cx);
6374 editor.end_transaction_at(now, cx);
6375 editor.undo(&Undo, cx);
6376 assert_eq!(editor.text(cx), "12cde6");
6377 });
6378 }
6379
6380 #[gpui::test]
6381 fn test_selection_with_mouse(cx: &mut gpui::MutableAppContext) {
6382 cx.set_global(Settings::test(cx));
6383
6384 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
6385 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6386 editor.update(cx, |view, cx| {
6387 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
6388 });
6389 assert_eq!(
6390 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6391 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
6392 );
6393
6394 editor.update(cx, |view, cx| {
6395 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6396 });
6397
6398 assert_eq!(
6399 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6400 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6401 );
6402
6403 editor.update(cx, |view, cx| {
6404 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6405 });
6406
6407 assert_eq!(
6408 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6409 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
6410 );
6411
6412 editor.update(cx, |view, cx| {
6413 view.end_selection(cx);
6414 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6415 });
6416
6417 assert_eq!(
6418 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6419 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
6420 );
6421
6422 editor.update(cx, |view, cx| {
6423 view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx);
6424 view.update_selection(DisplayPoint::new(0, 0), 0, Vector2F::zero(), cx);
6425 });
6426
6427 assert_eq!(
6428 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6429 [
6430 DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
6431 DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
6432 ]
6433 );
6434
6435 editor.update(cx, |view, cx| {
6436 view.end_selection(cx);
6437 });
6438
6439 assert_eq!(
6440 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6441 [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
6442 );
6443 }
6444
6445 #[gpui::test]
6446 fn test_canceling_pending_selection(cx: &mut gpui::MutableAppContext) {
6447 cx.set_global(Settings::test(cx));
6448 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
6449 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6450
6451 view.update(cx, |view, cx| {
6452 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
6453 assert_eq!(
6454 view.selections.display_ranges(cx),
6455 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
6456 );
6457 });
6458
6459 view.update(cx, |view, cx| {
6460 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6461 assert_eq!(
6462 view.selections.display_ranges(cx),
6463 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6464 );
6465 });
6466
6467 view.update(cx, |view, cx| {
6468 view.cancel(&Cancel, cx);
6469 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6470 assert_eq!(
6471 view.selections.display_ranges(cx),
6472 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6473 );
6474 });
6475 }
6476
6477 #[gpui::test]
6478 fn test_clone_with_selections(cx: &mut gpui::MutableAppContext) {
6479 let (text, selection_ranges) = marked_text_ranges(indoc! {"
6480 The qu[ick brown
6481 fox jum]ps over
6482 the lazy dog
6483 "});
6484 cx.set_global(Settings::test(cx));
6485 let buffer = MultiBuffer::build_simple(&text, cx);
6486
6487 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6488
6489 let cloned_editor = view.update(cx, |view, cx| {
6490 view.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
6491 view.clone(cx)
6492 });
6493
6494 assert_set_eq!(cloned_editor.selections.ranges(cx), selection_ranges);
6495 }
6496
6497 #[gpui::test]
6498 fn test_navigation_history(cx: &mut gpui::MutableAppContext) {
6499 cx.set_global(Settings::test(cx));
6500 use workspace::Item;
6501 let nav_history = Rc::new(RefCell::new(workspace::NavHistory::default()));
6502 let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
6503
6504 cx.add_window(Default::default(), |cx| {
6505 let mut editor = build_editor(buffer.clone(), cx);
6506 editor.nav_history = Some(ItemNavHistory::new(nav_history.clone(), &cx.handle()));
6507
6508 // Move the cursor a small distance.
6509 // Nothing is added to the navigation history.
6510 editor.change_selections(None, cx, |s| {
6511 s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)])
6512 });
6513 editor.change_selections(None, cx, |s| {
6514 s.select_display_ranges([DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)])
6515 });
6516 assert!(nav_history.borrow_mut().pop_backward().is_none());
6517
6518 // Move the cursor a large distance.
6519 // The history can jump back to the previous position.
6520 editor.change_selections(None, cx, |s| {
6521 s.select_display_ranges([DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)])
6522 });
6523 let nav_entry = nav_history.borrow_mut().pop_backward().unwrap();
6524 editor.navigate(nav_entry.data.unwrap(), cx);
6525 assert_eq!(nav_entry.item.id(), cx.view_id());
6526 assert_eq!(
6527 editor.selections.display_ranges(cx),
6528 &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]
6529 );
6530 assert!(nav_history.borrow_mut().pop_backward().is_none());
6531
6532 // Move the cursor a small distance via the mouse.
6533 // Nothing is added to the navigation history.
6534 editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx);
6535 editor.end_selection(cx);
6536 assert_eq!(
6537 editor.selections.display_ranges(cx),
6538 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
6539 );
6540 assert!(nav_history.borrow_mut().pop_backward().is_none());
6541
6542 // Move the cursor a large distance via the mouse.
6543 // The history can jump back to the previous position.
6544 editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx);
6545 editor.end_selection(cx);
6546 assert_eq!(
6547 editor.selections.display_ranges(cx),
6548 &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]
6549 );
6550 let nav_entry = nav_history.borrow_mut().pop_backward().unwrap();
6551 editor.navigate(nav_entry.data.unwrap(), cx);
6552 assert_eq!(nav_entry.item.id(), cx.view_id());
6553 assert_eq!(
6554 editor.selections.display_ranges(cx),
6555 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
6556 );
6557 assert!(nav_history.borrow_mut().pop_backward().is_none());
6558
6559 // Set scroll position to check later
6560 editor.set_scroll_position(Vector2F::new(5.5, 5.5), cx);
6561 let original_scroll_position = editor.scroll_position;
6562 let original_scroll_top_anchor = editor.scroll_top_anchor.clone();
6563
6564 // Jump to the end of the document and adjust scroll
6565 editor.move_to_end(&MoveToEnd, cx);
6566 editor.set_scroll_position(Vector2F::new(-2.5, -0.5), cx);
6567 assert_ne!(editor.scroll_position, original_scroll_position);
6568 assert_ne!(editor.scroll_top_anchor, original_scroll_top_anchor);
6569
6570 let nav_entry = nav_history.borrow_mut().pop_backward().unwrap();
6571 editor.navigate(nav_entry.data.unwrap(), cx);
6572 assert_eq!(editor.scroll_position, original_scroll_position);
6573 assert_eq!(editor.scroll_top_anchor, original_scroll_top_anchor);
6574
6575 // Ensure we don't panic when navigation data contains invalid anchors *and* points.
6576 let mut invalid_anchor = editor.scroll_top_anchor.clone();
6577 invalid_anchor.text_anchor.buffer_id = Some(999);
6578 let invalid_point = Point::new(9999, 0);
6579 editor.navigate(
6580 Box::new(NavigationData {
6581 cursor_anchor: invalid_anchor.clone(),
6582 cursor_position: invalid_point,
6583 scroll_top_anchor: invalid_anchor.clone(),
6584 scroll_top_row: invalid_point.row,
6585 scroll_position: Default::default(),
6586 }),
6587 cx,
6588 );
6589 assert_eq!(
6590 editor.selections.display_ranges(cx),
6591 &[editor.max_point(cx)..editor.max_point(cx)]
6592 );
6593 assert_eq!(
6594 editor.scroll_position(cx),
6595 vec2f(0., editor.max_point(cx).row() as f32)
6596 );
6597
6598 editor
6599 });
6600 }
6601
6602 #[gpui::test]
6603 fn test_cancel(cx: &mut gpui::MutableAppContext) {
6604 cx.set_global(Settings::test(cx));
6605 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
6606 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6607
6608 view.update(cx, |view, cx| {
6609 view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
6610 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6611 view.end_selection(cx);
6612
6613 view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx);
6614 view.update_selection(DisplayPoint::new(0, 3), 0, Vector2F::zero(), cx);
6615 view.end_selection(cx);
6616 assert_eq!(
6617 view.selections.display_ranges(cx),
6618 [
6619 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
6620 DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1),
6621 ]
6622 );
6623 });
6624
6625 view.update(cx, |view, cx| {
6626 view.cancel(&Cancel, cx);
6627 assert_eq!(
6628 view.selections.display_ranges(cx),
6629 [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)]
6630 );
6631 });
6632
6633 view.update(cx, |view, cx| {
6634 view.cancel(&Cancel, cx);
6635 assert_eq!(
6636 view.selections.display_ranges(cx),
6637 [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)]
6638 );
6639 });
6640 }
6641
6642 #[gpui::test]
6643 fn test_fold(cx: &mut gpui::MutableAppContext) {
6644 cx.set_global(Settings::test(cx));
6645 let buffer = MultiBuffer::build_simple(
6646 &"
6647 impl Foo {
6648 // Hello!
6649
6650 fn a() {
6651 1
6652 }
6653
6654 fn b() {
6655 2
6656 }
6657
6658 fn c() {
6659 3
6660 }
6661 }
6662 "
6663 .unindent(),
6664 cx,
6665 );
6666 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6667
6668 view.update(cx, |view, cx| {
6669 view.change_selections(None, cx, |s| {
6670 s.select_display_ranges([DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)]);
6671 });
6672 view.fold(&Fold, cx);
6673 assert_eq!(
6674 view.display_text(cx),
6675 "
6676 impl Foo {
6677 // Hello!
6678
6679 fn a() {
6680 1
6681 }
6682
6683 fn b() {…
6684 }
6685
6686 fn c() {…
6687 }
6688 }
6689 "
6690 .unindent(),
6691 );
6692
6693 view.fold(&Fold, cx);
6694 assert_eq!(
6695 view.display_text(cx),
6696 "
6697 impl Foo {…
6698 }
6699 "
6700 .unindent(),
6701 );
6702
6703 view.unfold_lines(&UnfoldLines, cx);
6704 assert_eq!(
6705 view.display_text(cx),
6706 "
6707 impl Foo {
6708 // Hello!
6709
6710 fn a() {
6711 1
6712 }
6713
6714 fn b() {…
6715 }
6716
6717 fn c() {…
6718 }
6719 }
6720 "
6721 .unindent(),
6722 );
6723
6724 view.unfold_lines(&UnfoldLines, cx);
6725 assert_eq!(view.display_text(cx), buffer.read(cx).read(cx).text());
6726 });
6727 }
6728
6729 #[gpui::test]
6730 fn test_move_cursor(cx: &mut gpui::MutableAppContext) {
6731 cx.set_global(Settings::test(cx));
6732 let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
6733 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6734
6735 buffer.update(cx, |buffer, cx| {
6736 buffer.edit(
6737 vec![
6738 (Point::new(1, 0)..Point::new(1, 0), "\t"),
6739 (Point::new(1, 1)..Point::new(1, 1), "\t"),
6740 ],
6741 cx,
6742 );
6743 });
6744
6745 view.update(cx, |view, cx| {
6746 assert_eq!(
6747 view.selections.display_ranges(cx),
6748 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
6749 );
6750
6751 view.move_down(&MoveDown, cx);
6752 assert_eq!(
6753 view.selections.display_ranges(cx),
6754 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
6755 );
6756
6757 view.move_right(&MoveRight, cx);
6758 assert_eq!(
6759 view.selections.display_ranges(cx),
6760 &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
6761 );
6762
6763 view.move_left(&MoveLeft, cx);
6764 assert_eq!(
6765 view.selections.display_ranges(cx),
6766 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
6767 );
6768
6769 view.move_up(&MoveUp, cx);
6770 assert_eq!(
6771 view.selections.display_ranges(cx),
6772 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
6773 );
6774
6775 view.move_to_end(&MoveToEnd, cx);
6776 assert_eq!(
6777 view.selections.display_ranges(cx),
6778 &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)]
6779 );
6780
6781 view.move_to_beginning(&MoveToBeginning, cx);
6782 assert_eq!(
6783 view.selections.display_ranges(cx),
6784 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
6785 );
6786
6787 view.change_selections(None, cx, |s| {
6788 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]);
6789 });
6790 view.select_to_beginning(&SelectToBeginning, cx);
6791 assert_eq!(
6792 view.selections.display_ranges(cx),
6793 &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)]
6794 );
6795
6796 view.select_to_end(&SelectToEnd, cx);
6797 assert_eq!(
6798 view.selections.display_ranges(cx),
6799 &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)]
6800 );
6801 });
6802 }
6803
6804 #[gpui::test]
6805 fn test_move_cursor_multibyte(cx: &mut gpui::MutableAppContext) {
6806 cx.set_global(Settings::test(cx));
6807 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx);
6808 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6809
6810 assert_eq!('ⓐ'.len_utf8(), 3);
6811 assert_eq!('α'.len_utf8(), 2);
6812
6813 view.update(cx, |view, cx| {
6814 view.fold_ranges(
6815 vec![
6816 Point::new(0, 6)..Point::new(0, 12),
6817 Point::new(1, 2)..Point::new(1, 4),
6818 Point::new(2, 4)..Point::new(2, 8),
6819 ],
6820 cx,
6821 );
6822 assert_eq!(view.display_text(cx), "ⓐⓑ…ⓔ\nab…e\nαβ…ε\n");
6823
6824 view.move_right(&MoveRight, cx);
6825 assert_eq!(
6826 view.selections.display_ranges(cx),
6827 &[empty_range(0, "ⓐ".len())]
6828 );
6829 view.move_right(&MoveRight, cx);
6830 assert_eq!(
6831 view.selections.display_ranges(cx),
6832 &[empty_range(0, "ⓐⓑ".len())]
6833 );
6834 view.move_right(&MoveRight, cx);
6835 assert_eq!(
6836 view.selections.display_ranges(cx),
6837 &[empty_range(0, "ⓐⓑ…".len())]
6838 );
6839
6840 view.move_down(&MoveDown, cx);
6841 assert_eq!(
6842 view.selections.display_ranges(cx),
6843 &[empty_range(1, "ab…".len())]
6844 );
6845 view.move_left(&MoveLeft, cx);
6846 assert_eq!(
6847 view.selections.display_ranges(cx),
6848 &[empty_range(1, "ab".len())]
6849 );
6850 view.move_left(&MoveLeft, cx);
6851 assert_eq!(
6852 view.selections.display_ranges(cx),
6853 &[empty_range(1, "a".len())]
6854 );
6855
6856 view.move_down(&MoveDown, cx);
6857 assert_eq!(
6858 view.selections.display_ranges(cx),
6859 &[empty_range(2, "α".len())]
6860 );
6861 view.move_right(&MoveRight, cx);
6862 assert_eq!(
6863 view.selections.display_ranges(cx),
6864 &[empty_range(2, "αβ".len())]
6865 );
6866 view.move_right(&MoveRight, cx);
6867 assert_eq!(
6868 view.selections.display_ranges(cx),
6869 &[empty_range(2, "αβ…".len())]
6870 );
6871 view.move_right(&MoveRight, cx);
6872 assert_eq!(
6873 view.selections.display_ranges(cx),
6874 &[empty_range(2, "αβ…ε".len())]
6875 );
6876
6877 view.move_up(&MoveUp, cx);
6878 assert_eq!(
6879 view.selections.display_ranges(cx),
6880 &[empty_range(1, "ab…e".len())]
6881 );
6882 view.move_up(&MoveUp, cx);
6883 assert_eq!(
6884 view.selections.display_ranges(cx),
6885 &[empty_range(0, "ⓐⓑ…ⓔ".len())]
6886 );
6887 view.move_left(&MoveLeft, cx);
6888 assert_eq!(
6889 view.selections.display_ranges(cx),
6890 &[empty_range(0, "ⓐⓑ…".len())]
6891 );
6892 view.move_left(&MoveLeft, cx);
6893 assert_eq!(
6894 view.selections.display_ranges(cx),
6895 &[empty_range(0, "ⓐⓑ".len())]
6896 );
6897 view.move_left(&MoveLeft, cx);
6898 assert_eq!(
6899 view.selections.display_ranges(cx),
6900 &[empty_range(0, "ⓐ".len())]
6901 );
6902 });
6903 }
6904
6905 #[gpui::test]
6906 fn test_move_cursor_different_line_lengths(cx: &mut gpui::MutableAppContext) {
6907 cx.set_global(Settings::test(cx));
6908 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
6909 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6910 view.update(cx, |view, cx| {
6911 view.change_selections(None, cx, |s| {
6912 s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
6913 });
6914 view.move_down(&MoveDown, cx);
6915 assert_eq!(
6916 view.selections.display_ranges(cx),
6917 &[empty_range(1, "abcd".len())]
6918 );
6919
6920 view.move_down(&MoveDown, cx);
6921 assert_eq!(
6922 view.selections.display_ranges(cx),
6923 &[empty_range(2, "αβγ".len())]
6924 );
6925
6926 view.move_down(&MoveDown, cx);
6927 assert_eq!(
6928 view.selections.display_ranges(cx),
6929 &[empty_range(3, "abcd".len())]
6930 );
6931
6932 view.move_down(&MoveDown, cx);
6933 assert_eq!(
6934 view.selections.display_ranges(cx),
6935 &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
6936 );
6937
6938 view.move_up(&MoveUp, cx);
6939 assert_eq!(
6940 view.selections.display_ranges(cx),
6941 &[empty_range(3, "abcd".len())]
6942 );
6943
6944 view.move_up(&MoveUp, cx);
6945 assert_eq!(
6946 view.selections.display_ranges(cx),
6947 &[empty_range(2, "αβγ".len())]
6948 );
6949 });
6950 }
6951
6952 #[gpui::test]
6953 fn test_beginning_end_of_line(cx: &mut gpui::MutableAppContext) {
6954 cx.set_global(Settings::test(cx));
6955 let buffer = MultiBuffer::build_simple("abc\n def", cx);
6956 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6957 view.update(cx, |view, cx| {
6958 view.change_selections(None, cx, |s| {
6959 s.select_display_ranges([
6960 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
6961 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
6962 ]);
6963 });
6964 });
6965
6966 view.update(cx, |view, cx| {
6967 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
6968 assert_eq!(
6969 view.selections.display_ranges(cx),
6970 &[
6971 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
6972 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
6973 ]
6974 );
6975 });
6976
6977 view.update(cx, |view, cx| {
6978 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
6979 assert_eq!(
6980 view.selections.display_ranges(cx),
6981 &[
6982 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
6983 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
6984 ]
6985 );
6986 });
6987
6988 view.update(cx, |view, cx| {
6989 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
6990 assert_eq!(
6991 view.selections.display_ranges(cx),
6992 &[
6993 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
6994 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
6995 ]
6996 );
6997 });
6998
6999 view.update(cx, |view, cx| {
7000 view.move_to_end_of_line(&MoveToEndOfLine, cx);
7001 assert_eq!(
7002 view.selections.display_ranges(cx),
7003 &[
7004 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7005 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
7006 ]
7007 );
7008 });
7009
7010 // Moving to the end of line again is a no-op.
7011 view.update(cx, |view, cx| {
7012 view.move_to_end_of_line(&MoveToEndOfLine, cx);
7013 assert_eq!(
7014 view.selections.display_ranges(cx),
7015 &[
7016 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7017 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
7018 ]
7019 );
7020 });
7021
7022 view.update(cx, |view, cx| {
7023 view.move_left(&MoveLeft, cx);
7024 view.select_to_beginning_of_line(
7025 &SelectToBeginningOfLine {
7026 stop_at_soft_wraps: true,
7027 },
7028 cx,
7029 );
7030 assert_eq!(
7031 view.selections.display_ranges(cx),
7032 &[
7033 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7034 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7035 ]
7036 );
7037 });
7038
7039 view.update(cx, |view, cx| {
7040 view.select_to_beginning_of_line(
7041 &SelectToBeginningOfLine {
7042 stop_at_soft_wraps: true,
7043 },
7044 cx,
7045 );
7046 assert_eq!(
7047 view.selections.display_ranges(cx),
7048 &[
7049 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7050 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0),
7051 ]
7052 );
7053 });
7054
7055 view.update(cx, |view, cx| {
7056 view.select_to_beginning_of_line(
7057 &SelectToBeginningOfLine {
7058 stop_at_soft_wraps: true,
7059 },
7060 cx,
7061 );
7062 assert_eq!(
7063 view.selections.display_ranges(cx),
7064 &[
7065 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7066 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7067 ]
7068 );
7069 });
7070
7071 view.update(cx, |view, cx| {
7072 view.select_to_end_of_line(
7073 &SelectToEndOfLine {
7074 stop_at_soft_wraps: true,
7075 },
7076 cx,
7077 );
7078 assert_eq!(
7079 view.selections.display_ranges(cx),
7080 &[
7081 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
7082 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5),
7083 ]
7084 );
7085 });
7086
7087 view.update(cx, |view, cx| {
7088 view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
7089 assert_eq!(view.display_text(cx), "ab\n de");
7090 assert_eq!(
7091 view.selections.display_ranges(cx),
7092 &[
7093 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7094 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
7095 ]
7096 );
7097 });
7098
7099 view.update(cx, |view, cx| {
7100 view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
7101 assert_eq!(view.display_text(cx), "\n");
7102 assert_eq!(
7103 view.selections.display_ranges(cx),
7104 &[
7105 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7106 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7107 ]
7108 );
7109 });
7110 }
7111
7112 #[gpui::test]
7113 fn test_prev_next_word_boundary(cx: &mut gpui::MutableAppContext) {
7114 cx.set_global(Settings::test(cx));
7115 let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx);
7116 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7117 view.update(cx, |view, cx| {
7118 view.change_selections(None, cx, |s| {
7119 s.select_display_ranges([
7120 DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
7121 DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
7122 ])
7123 });
7124
7125 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7126 assert_selection_ranges(
7127 "use std::<>str::{foo, bar}\n\n {[]baz.qux()}",
7128 vec![('<', '>'), ('[', ']')],
7129 view,
7130 cx,
7131 );
7132
7133 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7134 assert_selection_ranges(
7135 "use std<>::str::{foo, bar}\n\n []{baz.qux()}",
7136 vec![('<', '>'), ('[', ']')],
7137 view,
7138 cx,
7139 );
7140
7141 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7142 assert_selection_ranges(
7143 "use <>std::str::{foo, bar}\n\n[] {baz.qux()}",
7144 vec![('<', '>'), ('[', ']')],
7145 view,
7146 cx,
7147 );
7148
7149 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7150 assert_selection_ranges(
7151 "<>use std::str::{foo, bar}\n[]\n {baz.qux()}",
7152 vec![('<', '>'), ('[', ']')],
7153 view,
7154 cx,
7155 );
7156
7157 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7158 assert_selection_ranges(
7159 "<>use std::str::{foo, bar[]}\n\n {baz.qux()}",
7160 vec![('<', '>'), ('[', ']')],
7161 view,
7162 cx,
7163 );
7164
7165 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7166 assert_selection_ranges(
7167 "use<> std::str::{foo, bar}[]\n\n {baz.qux()}",
7168 vec![('<', '>'), ('[', ']')],
7169 view,
7170 cx,
7171 );
7172
7173 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7174 assert_selection_ranges(
7175 "use std<>::str::{foo, bar}\n[]\n {baz.qux()}",
7176 vec![('<', '>'), ('[', ']')],
7177 view,
7178 cx,
7179 );
7180
7181 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7182 assert_selection_ranges(
7183 "use std::<>str::{foo, bar}\n\n {[]baz.qux()}",
7184 vec![('<', '>'), ('[', ']')],
7185 view,
7186 cx,
7187 );
7188
7189 view.move_right(&MoveRight, cx);
7190 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7191 assert_selection_ranges(
7192 "use std::>s<tr::{foo, bar}\n\n {]b[az.qux()}",
7193 vec![('<', '>'), ('[', ']')],
7194 view,
7195 cx,
7196 );
7197
7198 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7199 assert_selection_ranges(
7200 "use std>::s<tr::{foo, bar}\n\n ]{b[az.qux()}",
7201 vec![('<', '>'), ('[', ']')],
7202 view,
7203 cx,
7204 );
7205
7206 view.select_to_next_word_end(&SelectToNextWordEnd, cx);
7207 assert_selection_ranges(
7208 "use std::>s<tr::{foo, bar}\n\n {]b[az.qux()}",
7209 vec![('<', '>'), ('[', ']')],
7210 view,
7211 cx,
7212 );
7213 });
7214 }
7215
7216 #[gpui::test]
7217 fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut gpui::MutableAppContext) {
7218 cx.set_global(Settings::test(cx));
7219 let buffer = MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx);
7220 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7221
7222 view.update(cx, |view, cx| {
7223 view.set_wrap_width(Some(140.), cx);
7224 assert_eq!(
7225 view.display_text(cx),
7226 "use one::{\n two::three::\n four::five\n};"
7227 );
7228
7229 view.change_selections(None, cx, |s| {
7230 s.select_display_ranges([DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)]);
7231 });
7232
7233 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7234 assert_eq!(
7235 view.selections.display_ranges(cx),
7236 &[DisplayPoint::new(1, 9)..DisplayPoint::new(1, 9)]
7237 );
7238
7239 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7240 assert_eq!(
7241 view.selections.display_ranges(cx),
7242 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
7243 );
7244
7245 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7246 assert_eq!(
7247 view.selections.display_ranges(cx),
7248 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7249 );
7250
7251 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7252 assert_eq!(
7253 view.selections.display_ranges(cx),
7254 &[DisplayPoint::new(2, 8)..DisplayPoint::new(2, 8)]
7255 );
7256
7257 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7258 assert_eq!(
7259 view.selections.display_ranges(cx),
7260 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7261 );
7262
7263 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7264 assert_eq!(
7265 view.selections.display_ranges(cx),
7266 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
7267 );
7268 });
7269 }
7270
7271 #[gpui::test]
7272 fn test_delete_to_beginning_of_line(cx: &mut gpui::MutableAppContext) {
7273 cx.set_global(Settings::test(cx));
7274 let (text, ranges) = marked_text_ranges("one [two three] four");
7275 let buffer = MultiBuffer::build_simple(&text, cx);
7276
7277 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7278
7279 editor.update(cx, |editor, cx| {
7280 editor.change_selections(None, cx, |s| s.select_ranges(ranges));
7281 editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
7282 assert_eq!(editor.text(cx), " four");
7283 });
7284 }
7285
7286 #[gpui::test]
7287 fn test_delete_to_word_boundary(cx: &mut gpui::MutableAppContext) {
7288 cx.set_global(Settings::test(cx));
7289 let buffer = MultiBuffer::build_simple("one two three four", cx);
7290 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7291
7292 view.update(cx, |view, cx| {
7293 view.change_selections(None, cx, |s| {
7294 s.select_display_ranges([
7295 // an empty selection - the preceding word fragment is deleted
7296 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7297 // characters selected - they are deleted
7298 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 12),
7299 ])
7300 });
7301 view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
7302 });
7303
7304 assert_eq!(buffer.read(cx).read(cx).text(), "e two te four");
7305
7306 view.update(cx, |view, cx| {
7307 view.change_selections(None, cx, |s| {
7308 s.select_display_ranges([
7309 // an empty selection - the following word fragment is deleted
7310 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7311 // characters selected - they are deleted
7312 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 10),
7313 ])
7314 });
7315 view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
7316 });
7317
7318 assert_eq!(buffer.read(cx).read(cx).text(), "e t te our");
7319 }
7320
7321 #[gpui::test]
7322 fn test_newline(cx: &mut gpui::MutableAppContext) {
7323 cx.set_global(Settings::test(cx));
7324 let buffer = MultiBuffer::build_simple("aaaa\n bbbb\n", cx);
7325 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7326
7327 view.update(cx, |view, cx| {
7328 view.change_selections(None, cx, |s| {
7329 s.select_display_ranges([
7330 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7331 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7332 DisplayPoint::new(1, 6)..DisplayPoint::new(1, 6),
7333 ])
7334 });
7335
7336 view.newline(&Newline, cx);
7337 assert_eq!(view.text(cx), "aa\naa\n \n bb\n bb\n");
7338 });
7339 }
7340
7341 #[gpui::test]
7342 fn test_newline_with_old_selections(cx: &mut gpui::MutableAppContext) {
7343 cx.set_global(Settings::test(cx));
7344 let buffer = MultiBuffer::build_simple(
7345 "
7346 a
7347 b(
7348 X
7349 )
7350 c(
7351 X
7352 )
7353 "
7354 .unindent()
7355 .as_str(),
7356 cx,
7357 );
7358
7359 let (_, editor) = cx.add_window(Default::default(), |cx| {
7360 let mut editor = build_editor(buffer.clone(), cx);
7361 editor.change_selections(None, cx, |s| {
7362 s.select_ranges([
7363 Point::new(2, 4)..Point::new(2, 5),
7364 Point::new(5, 4)..Point::new(5, 5),
7365 ])
7366 });
7367 editor
7368 });
7369
7370 // Edit the buffer directly, deleting ranges surrounding the editor's selections
7371 buffer.update(cx, |buffer, cx| {
7372 buffer.edit(
7373 [
7374 (Point::new(1, 2)..Point::new(3, 0), ""),
7375 (Point::new(4, 2)..Point::new(6, 0), ""),
7376 ],
7377 cx,
7378 );
7379 assert_eq!(
7380 buffer.read(cx).text(),
7381 "
7382 a
7383 b()
7384 c()
7385 "
7386 .unindent()
7387 );
7388 });
7389
7390 editor.update(cx, |editor, cx| {
7391 assert_eq!(
7392 editor.selections.ranges(cx),
7393 &[
7394 Point::new(1, 2)..Point::new(1, 2),
7395 Point::new(2, 2)..Point::new(2, 2),
7396 ],
7397 );
7398
7399 editor.newline(&Newline, cx);
7400 assert_eq!(
7401 editor.text(cx),
7402 "
7403 a
7404 b(
7405 )
7406 c(
7407 )
7408 "
7409 .unindent()
7410 );
7411
7412 // The selections are moved after the inserted newlines
7413 assert_eq!(
7414 editor.selections.ranges(cx),
7415 &[
7416 Point::new(2, 0)..Point::new(2, 0),
7417 Point::new(4, 0)..Point::new(4, 0),
7418 ],
7419 );
7420 });
7421 }
7422
7423 #[gpui::test]
7424 fn test_insert_with_old_selections(cx: &mut gpui::MutableAppContext) {
7425 cx.set_global(Settings::test(cx));
7426 let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
7427 let (_, editor) = cx.add_window(Default::default(), |cx| {
7428 let mut editor = build_editor(buffer.clone(), cx);
7429 editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
7430 editor
7431 });
7432
7433 // Edit the buffer directly, deleting ranges surrounding the editor's selections
7434 buffer.update(cx, |buffer, cx| {
7435 buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], cx);
7436 assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
7437 });
7438
7439 editor.update(cx, |editor, cx| {
7440 assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
7441
7442 editor.insert("Z", cx);
7443 assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
7444
7445 // The selections are moved after the inserted characters
7446 assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
7447 });
7448 }
7449
7450 #[gpui::test]
7451 async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
7452 let mut cx = EditorTestContext::new(cx).await;
7453
7454 cx.set_state(indoc! {"
7455 [one} [two}
7456 three
7457 four"});
7458 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7459 cx.assert_editor_state(indoc! {"
7460 [one} [two}
7461 three
7462 four"});
7463
7464 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7465 cx.assert_editor_state(indoc! {"
7466 [one} [two}
7467 three
7468 four"});
7469
7470 // select across line ending
7471 cx.set_state(indoc! {"
7472 one two
7473 t[hree
7474 } four"});
7475 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7476 cx.assert_editor_state(indoc! {"
7477 one two
7478 t[hree
7479 } four"});
7480
7481 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7482 cx.assert_editor_state(indoc! {"
7483 one two
7484 t[hree
7485 } four"});
7486
7487 // Ensure that indenting/outdenting works when the cursor is at column 0.
7488 cx.set_state(indoc! {"
7489 one two
7490 |three
7491 four"});
7492 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7493 cx.assert_editor_state(indoc! {"
7494 one two
7495 |three
7496 four"});
7497
7498 cx.set_state(indoc! {"
7499 one two
7500 | three
7501 four"});
7502 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7503 cx.assert_editor_state(indoc! {"
7504 one two
7505 |three
7506 four"});
7507 }
7508
7509 #[gpui::test]
7510 fn test_indent_outdent_with_excerpts(cx: &mut gpui::MutableAppContext) {
7511 cx.set_global(
7512 Settings::test(cx)
7513 .with_overrides(
7514 "TOML",
7515 LanguageOverride {
7516 tab_size: Some(2),
7517 ..Default::default()
7518 },
7519 )
7520 .with_overrides(
7521 "Rust",
7522 LanguageOverride {
7523 tab_size: Some(4),
7524 ..Default::default()
7525 },
7526 ),
7527 );
7528 let toml_language = Arc::new(Language::new(
7529 LanguageConfig {
7530 name: "TOML".into(),
7531 ..Default::default()
7532 },
7533 None,
7534 ));
7535 let rust_language = Arc::new(Language::new(
7536 LanguageConfig {
7537 name: "Rust".into(),
7538 ..Default::default()
7539 },
7540 None,
7541 ));
7542
7543 let toml_buffer = cx
7544 .add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx).with_language(toml_language, cx));
7545 let rust_buffer = cx.add_model(|cx| {
7546 Buffer::new(0, "const c: usize = 3;\n", cx).with_language(rust_language, cx)
7547 });
7548 let multibuffer = cx.add_model(|cx| {
7549 let mut multibuffer = MultiBuffer::new(0);
7550 multibuffer.push_excerpts(
7551 toml_buffer.clone(),
7552 [Point::new(0, 0)..Point::new(2, 0)],
7553 cx,
7554 );
7555 multibuffer.push_excerpts(
7556 rust_buffer.clone(),
7557 [Point::new(0, 0)..Point::new(1, 0)],
7558 cx,
7559 );
7560 multibuffer
7561 });
7562
7563 cx.add_window(Default::default(), |cx| {
7564 let mut editor = build_editor(multibuffer, cx);
7565
7566 assert_eq!(
7567 editor.text(cx),
7568 indoc! {"
7569 a = 1
7570 b = 2
7571
7572 const c: usize = 3;
7573 "}
7574 );
7575
7576 select_ranges(
7577 &mut editor,
7578 indoc! {"
7579 [a] = 1
7580 b = 2
7581
7582 [const c:] usize = 3;
7583 "},
7584 cx,
7585 );
7586
7587 editor.tab(&Tab, cx);
7588 assert_text_with_selections(
7589 &mut editor,
7590 indoc! {"
7591 [a] = 1
7592 b = 2
7593
7594 [const c:] usize = 3;
7595 "},
7596 cx,
7597 );
7598 editor.tab_prev(&TabPrev, cx);
7599 assert_text_with_selections(
7600 &mut editor,
7601 indoc! {"
7602 [a] = 1
7603 b = 2
7604
7605 [const c:] usize = 3;
7606 "},
7607 cx,
7608 );
7609
7610 editor
7611 });
7612 }
7613
7614 #[gpui::test]
7615 async fn test_backspace(cx: &mut gpui::TestAppContext) {
7616 let mut cx = EditorTestContext::new(cx).await;
7617 // Basic backspace
7618 cx.set_state(indoc! {"
7619 on|e two three
7620 fou[r} five six
7621 seven {eight nine
7622 ]ten"});
7623 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
7624 cx.assert_editor_state(indoc! {"
7625 o|e two three
7626 fou| five six
7627 seven |ten"});
7628
7629 // Test backspace inside and around indents
7630 cx.set_state(indoc! {"
7631 zero
7632 |one
7633 |two
7634 | | | three
7635 | | four"});
7636 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
7637 cx.assert_editor_state(indoc! {"
7638 zero
7639 |one
7640 |two
7641 | three| four"});
7642
7643 // Test backspace with line_mode set to true
7644 cx.update_editor(|e, _| e.selections.line_mode = true);
7645 cx.set_state(indoc! {"
7646 The |quick |brown
7647 fox jumps over
7648 the lazy dog
7649 |The qu[ick b}rown"});
7650 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
7651 cx.assert_editor_state(indoc! {"
7652 |fox jumps over
7653 the lazy dog|"});
7654 }
7655
7656 #[gpui::test]
7657 async fn test_delete(cx: &mut gpui::TestAppContext) {
7658 let mut cx = EditorTestContext::new(cx).await;
7659
7660 cx.set_state(indoc! {"
7661 on|e two three
7662 fou[r} five six
7663 seven {eight nine
7664 ]ten"});
7665 cx.update_editor(|e, cx| e.delete(&Delete, cx));
7666 cx.assert_editor_state(indoc! {"
7667 on| two three
7668 fou| five six
7669 seven |ten"});
7670
7671 // Test backspace with line_mode set to true
7672 cx.update_editor(|e, _| e.selections.line_mode = true);
7673 cx.set_state(indoc! {"
7674 The |quick |brown
7675 fox {jum]ps over
7676 the lazy dog
7677 |The qu[ick b}rown"});
7678 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
7679 cx.assert_editor_state("|the lazy dog|");
7680 }
7681
7682 #[gpui::test]
7683 fn test_delete_line(cx: &mut gpui::MutableAppContext) {
7684 cx.set_global(Settings::test(cx));
7685 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7686 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7687 view.update(cx, |view, cx| {
7688 view.change_selections(None, cx, |s| {
7689 s.select_display_ranges([
7690 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7691 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
7692 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
7693 ])
7694 });
7695 view.delete_line(&DeleteLine, cx);
7696 assert_eq!(view.display_text(cx), "ghi");
7697 assert_eq!(
7698 view.selections.display_ranges(cx),
7699 vec![
7700 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7701 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
7702 ]
7703 );
7704 });
7705
7706 cx.set_global(Settings::test(cx));
7707 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7708 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7709 view.update(cx, |view, cx| {
7710 view.change_selections(None, cx, |s| {
7711 s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)])
7712 });
7713 view.delete_line(&DeleteLine, cx);
7714 assert_eq!(view.display_text(cx), "ghi\n");
7715 assert_eq!(
7716 view.selections.display_ranges(cx),
7717 vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
7718 );
7719 });
7720 }
7721
7722 #[gpui::test]
7723 fn test_duplicate_line(cx: &mut gpui::MutableAppContext) {
7724 cx.set_global(Settings::test(cx));
7725 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7726 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7727 view.update(cx, |view, cx| {
7728 view.change_selections(None, cx, |s| {
7729 s.select_display_ranges([
7730 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
7731 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7732 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7733 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
7734 ])
7735 });
7736 view.duplicate_line(&DuplicateLine, cx);
7737 assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
7738 assert_eq!(
7739 view.selections.display_ranges(cx),
7740 vec![
7741 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
7742 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7743 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
7744 DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
7745 ]
7746 );
7747 });
7748
7749 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7750 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7751 view.update(cx, |view, cx| {
7752 view.change_selections(None, cx, |s| {
7753 s.select_display_ranges([
7754 DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
7755 DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
7756 ])
7757 });
7758 view.duplicate_line(&DuplicateLine, cx);
7759 assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
7760 assert_eq!(
7761 view.selections.display_ranges(cx),
7762 vec![
7763 DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
7764 DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
7765 ]
7766 );
7767 });
7768 }
7769
7770 #[gpui::test]
7771 fn test_move_line_up_down(cx: &mut gpui::MutableAppContext) {
7772 cx.set_global(Settings::test(cx));
7773 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
7774 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7775 view.update(cx, |view, cx| {
7776 view.fold_ranges(
7777 vec![
7778 Point::new(0, 2)..Point::new(1, 2),
7779 Point::new(2, 3)..Point::new(4, 1),
7780 Point::new(7, 0)..Point::new(8, 4),
7781 ],
7782 cx,
7783 );
7784 view.change_selections(None, cx, |s| {
7785 s.select_display_ranges([
7786 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7787 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
7788 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
7789 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
7790 ])
7791 });
7792 assert_eq!(
7793 view.display_text(cx),
7794 "aa…bbb\nccc…eeee\nfffff\nggggg\n…i\njjjjj"
7795 );
7796
7797 view.move_line_up(&MoveLineUp, cx);
7798 assert_eq!(
7799 view.display_text(cx),
7800 "aa…bbb\nccc…eeee\nggggg\n…i\njjjjj\nfffff"
7801 );
7802 assert_eq!(
7803 view.selections.display_ranges(cx),
7804 vec![
7805 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7806 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
7807 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
7808 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
7809 ]
7810 );
7811 });
7812
7813 view.update(cx, |view, cx| {
7814 view.move_line_down(&MoveLineDown, cx);
7815 assert_eq!(
7816 view.display_text(cx),
7817 "ccc…eeee\naa…bbb\nfffff\nggggg\n…i\njjjjj"
7818 );
7819 assert_eq!(
7820 view.selections.display_ranges(cx),
7821 vec![
7822 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
7823 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
7824 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
7825 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
7826 ]
7827 );
7828 });
7829
7830 view.update(cx, |view, cx| {
7831 view.move_line_down(&MoveLineDown, cx);
7832 assert_eq!(
7833 view.display_text(cx),
7834 "ccc…eeee\nfffff\naa…bbb\nggggg\n…i\njjjjj"
7835 );
7836 assert_eq!(
7837 view.selections.display_ranges(cx),
7838 vec![
7839 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
7840 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
7841 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
7842 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
7843 ]
7844 );
7845 });
7846
7847 view.update(cx, |view, cx| {
7848 view.move_line_up(&MoveLineUp, cx);
7849 assert_eq!(
7850 view.display_text(cx),
7851 "ccc…eeee\naa…bbb\nggggg\n…i\njjjjj\nfffff"
7852 );
7853 assert_eq!(
7854 view.selections.display_ranges(cx),
7855 vec![
7856 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
7857 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
7858 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
7859 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
7860 ]
7861 );
7862 });
7863 }
7864
7865 #[gpui::test]
7866 fn test_move_line_up_down_with_blocks(cx: &mut gpui::MutableAppContext) {
7867 cx.set_global(Settings::test(cx));
7868 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
7869 let snapshot = buffer.read(cx).snapshot(cx);
7870 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7871 editor.update(cx, |editor, cx| {
7872 editor.insert_blocks(
7873 [BlockProperties {
7874 position: snapshot.anchor_after(Point::new(2, 0)),
7875 disposition: BlockDisposition::Below,
7876 height: 1,
7877 render: Arc::new(|_| Empty::new().boxed()),
7878 }],
7879 cx,
7880 );
7881 editor.change_selections(None, cx, |s| {
7882 s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
7883 });
7884 editor.move_line_down(&MoveLineDown, cx);
7885 });
7886 }
7887
7888 #[gpui::test]
7889 fn test_transpose(cx: &mut gpui::MutableAppContext) {
7890 cx.set_global(Settings::test(cx));
7891
7892 cx.add_window(Default::default(), |cx| {
7893 let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
7894
7895 editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
7896 editor.transpose(&Default::default(), cx);
7897 assert_eq!(editor.text(cx), "bac");
7898 assert_eq!(editor.selections.ranges(cx), [2..2]);
7899
7900 editor.transpose(&Default::default(), cx);
7901 assert_eq!(editor.text(cx), "bca");
7902 assert_eq!(editor.selections.ranges(cx), [3..3]);
7903
7904 editor.transpose(&Default::default(), cx);
7905 assert_eq!(editor.text(cx), "bac");
7906 assert_eq!(editor.selections.ranges(cx), [3..3]);
7907
7908 editor
7909 })
7910 .1;
7911
7912 cx.add_window(Default::default(), |cx| {
7913 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
7914
7915 editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
7916 editor.transpose(&Default::default(), cx);
7917 assert_eq!(editor.text(cx), "acb\nde");
7918 assert_eq!(editor.selections.ranges(cx), [3..3]);
7919
7920 editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
7921 editor.transpose(&Default::default(), cx);
7922 assert_eq!(editor.text(cx), "acbd\ne");
7923 assert_eq!(editor.selections.ranges(cx), [5..5]);
7924
7925 editor.transpose(&Default::default(), cx);
7926 assert_eq!(editor.text(cx), "acbde\n");
7927 assert_eq!(editor.selections.ranges(cx), [6..6]);
7928
7929 editor.transpose(&Default::default(), cx);
7930 assert_eq!(editor.text(cx), "acbd\ne");
7931 assert_eq!(editor.selections.ranges(cx), [6..6]);
7932
7933 editor
7934 })
7935 .1;
7936
7937 cx.add_window(Default::default(), |cx| {
7938 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
7939
7940 editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
7941 editor.transpose(&Default::default(), cx);
7942 assert_eq!(editor.text(cx), "bacd\ne");
7943 assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
7944
7945 editor.transpose(&Default::default(), cx);
7946 assert_eq!(editor.text(cx), "bcade\n");
7947 assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
7948
7949 editor.transpose(&Default::default(), cx);
7950 assert_eq!(editor.text(cx), "bcda\ne");
7951 assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
7952
7953 editor.transpose(&Default::default(), cx);
7954 assert_eq!(editor.text(cx), "bcade\n");
7955 assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
7956
7957 editor.transpose(&Default::default(), cx);
7958 assert_eq!(editor.text(cx), "bcaed\n");
7959 assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
7960
7961 editor
7962 })
7963 .1;
7964
7965 cx.add_window(Default::default(), |cx| {
7966 let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
7967
7968 editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
7969 editor.transpose(&Default::default(), cx);
7970 assert_eq!(editor.text(cx), "🏀🍐✋");
7971 assert_eq!(editor.selections.ranges(cx), [8..8]);
7972
7973 editor.transpose(&Default::default(), cx);
7974 assert_eq!(editor.text(cx), "🏀✋🍐");
7975 assert_eq!(editor.selections.ranges(cx), [11..11]);
7976
7977 editor.transpose(&Default::default(), cx);
7978 assert_eq!(editor.text(cx), "🏀🍐✋");
7979 assert_eq!(editor.selections.ranges(cx), [11..11]);
7980
7981 editor
7982 })
7983 .1;
7984 }
7985
7986 #[gpui::test]
7987 async fn test_clipboard(cx: &mut gpui::TestAppContext) {
7988 let mut cx = EditorTestContext::new(cx).await;
7989
7990 cx.set_state("[one✅ }two [three }four [five }six ");
7991 cx.update_editor(|e, cx| e.cut(&Cut, cx));
7992 cx.assert_editor_state("|two |four |six ");
7993
7994 // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
7995 cx.set_state("two |four |six |");
7996 cx.update_editor(|e, cx| e.paste(&Paste, cx));
7997 cx.assert_editor_state("two one✅ |four three |six five |");
7998
7999 // Paste again but with only two cursors. Since the number of cursors doesn't
8000 // match the number of slices in the clipboard, the entire clipboard text
8001 // is pasted at each cursor.
8002 cx.set_state("|two one✅ four three six five |");
8003 cx.update_editor(|e, cx| {
8004 e.handle_input(&Input("( ".into()), cx);
8005 e.paste(&Paste, cx);
8006 e.handle_input(&Input(") ".into()), cx);
8007 });
8008 cx.assert_editor_state(indoc! {"
8009 ( one✅
8010 three
8011 five ) |two one✅ four three six five ( one✅
8012 three
8013 five ) |"});
8014
8015 // Cut with three selections, one of which is full-line.
8016 cx.set_state(indoc! {"
8017 1[2}3
8018 4|567
8019 [8}9"});
8020 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8021 cx.assert_editor_state(indoc! {"
8022 1|3
8023 |9"});
8024
8025 // Paste with three selections, noticing how the copied selection that was full-line
8026 // gets inserted before the second cursor.
8027 cx.set_state(indoc! {"
8028 1|3
8029 9|
8030 [o}ne"});
8031 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8032 cx.assert_editor_state(indoc! {"
8033 12|3
8034 4567
8035 9|
8036 8|ne"});
8037
8038 // Copy with a single cursor only, which writes the whole line into the clipboard.
8039 cx.set_state(indoc! {"
8040 The quick brown
8041 fox ju|mps over
8042 the lazy dog"});
8043 cx.update_editor(|e, cx| e.copy(&Copy, cx));
8044 cx.assert_clipboard_content(Some("fox jumps over\n"));
8045
8046 // Paste with three selections, noticing how the copied full-line selection is inserted
8047 // before the empty selections but replaces the selection that is non-empty.
8048 cx.set_state(indoc! {"
8049 T|he quick brown
8050 [fo}x jumps over
8051 t|he lazy dog"});
8052 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8053 cx.assert_editor_state(indoc! {"
8054 fox jumps over
8055 T|he quick brown
8056 fox jumps over
8057 |x jumps over
8058 fox jumps over
8059 t|he lazy dog"});
8060 }
8061
8062 #[gpui::test]
8063 fn test_select_all(cx: &mut gpui::MutableAppContext) {
8064 cx.set_global(Settings::test(cx));
8065 let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
8066 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8067 view.update(cx, |view, cx| {
8068 view.select_all(&SelectAll, cx);
8069 assert_eq!(
8070 view.selections.display_ranges(cx),
8071 &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
8072 );
8073 });
8074 }
8075
8076 #[gpui::test]
8077 fn test_select_line(cx: &mut gpui::MutableAppContext) {
8078 cx.set_global(Settings::test(cx));
8079 let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
8080 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8081 view.update(cx, |view, cx| {
8082 view.change_selections(None, cx, |s| {
8083 s.select_display_ranges([
8084 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8085 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8086 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8087 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
8088 ])
8089 });
8090 view.select_line(&SelectLine, cx);
8091 assert_eq!(
8092 view.selections.display_ranges(cx),
8093 vec![
8094 DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
8095 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
8096 ]
8097 );
8098 });
8099
8100 view.update(cx, |view, cx| {
8101 view.select_line(&SelectLine, cx);
8102 assert_eq!(
8103 view.selections.display_ranges(cx),
8104 vec![
8105 DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
8106 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
8107 ]
8108 );
8109 });
8110
8111 view.update(cx, |view, cx| {
8112 view.select_line(&SelectLine, cx);
8113 assert_eq!(
8114 view.selections.display_ranges(cx),
8115 vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
8116 );
8117 });
8118 }
8119
8120 #[gpui::test]
8121 fn test_split_selection_into_lines(cx: &mut gpui::MutableAppContext) {
8122 cx.set_global(Settings::test(cx));
8123 let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
8124 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8125 view.update(cx, |view, cx| {
8126 view.fold_ranges(
8127 vec![
8128 Point::new(0, 2)..Point::new(1, 2),
8129 Point::new(2, 3)..Point::new(4, 1),
8130 Point::new(7, 0)..Point::new(8, 4),
8131 ],
8132 cx,
8133 );
8134 view.change_selections(None, cx, |s| {
8135 s.select_display_ranges([
8136 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8137 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8138 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8139 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
8140 ])
8141 });
8142 assert_eq!(view.display_text(cx), "aa…bbb\nccc…eeee\nfffff\nggggg\n…i");
8143 });
8144
8145 view.update(cx, |view, cx| {
8146 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
8147 assert_eq!(
8148 view.display_text(cx),
8149 "aaaaa\nbbbbb\nccc…eeee\nfffff\nggggg\n…i"
8150 );
8151 assert_eq!(
8152 view.selections.display_ranges(cx),
8153 [
8154 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8155 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8156 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
8157 DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
8158 ]
8159 );
8160 });
8161
8162 view.update(cx, |view, cx| {
8163 view.change_selections(None, cx, |s| {
8164 s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
8165 });
8166 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
8167 assert_eq!(
8168 view.display_text(cx),
8169 "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
8170 );
8171 assert_eq!(
8172 view.selections.display_ranges(cx),
8173 [
8174 DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
8175 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
8176 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
8177 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
8178 DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
8179 DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
8180 DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
8181 DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
8182 ]
8183 );
8184 });
8185 }
8186
8187 #[gpui::test]
8188 fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) {
8189 cx.set_global(Settings::test(cx));
8190 let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
8191 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8192
8193 view.update(cx, |view, cx| {
8194 view.change_selections(None, cx, |s| {
8195 s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)])
8196 });
8197 });
8198 view.update(cx, |view, cx| {
8199 view.add_selection_above(&AddSelectionAbove, cx);
8200 assert_eq!(
8201 view.selections.display_ranges(cx),
8202 vec![
8203 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8204 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8205 ]
8206 );
8207 });
8208
8209 view.update(cx, |view, cx| {
8210 view.add_selection_above(&AddSelectionAbove, cx);
8211 assert_eq!(
8212 view.selections.display_ranges(cx),
8213 vec![
8214 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8215 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8216 ]
8217 );
8218 });
8219
8220 view.update(cx, |view, cx| {
8221 view.add_selection_below(&AddSelectionBelow, cx);
8222 assert_eq!(
8223 view.selections.display_ranges(cx),
8224 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
8225 );
8226
8227 view.undo_selection(&UndoSelection, cx);
8228 assert_eq!(
8229 view.selections.display_ranges(cx),
8230 vec![
8231 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8232 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8233 ]
8234 );
8235
8236 view.redo_selection(&RedoSelection, cx);
8237 assert_eq!(
8238 view.selections.display_ranges(cx),
8239 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
8240 );
8241 });
8242
8243 view.update(cx, |view, cx| {
8244 view.add_selection_below(&AddSelectionBelow, cx);
8245 assert_eq!(
8246 view.selections.display_ranges(cx),
8247 vec![
8248 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
8249 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
8250 ]
8251 );
8252 });
8253
8254 view.update(cx, |view, cx| {
8255 view.add_selection_below(&AddSelectionBelow, cx);
8256 assert_eq!(
8257 view.selections.display_ranges(cx),
8258 vec![
8259 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
8260 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
8261 ]
8262 );
8263 });
8264
8265 view.update(cx, |view, cx| {
8266 view.change_selections(None, cx, |s| {
8267 s.select_display_ranges([DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)])
8268 });
8269 });
8270 view.update(cx, |view, cx| {
8271 view.add_selection_below(&AddSelectionBelow, cx);
8272 assert_eq!(
8273 view.selections.display_ranges(cx),
8274 vec![
8275 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
8276 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
8277 ]
8278 );
8279 });
8280
8281 view.update(cx, |view, cx| {
8282 view.add_selection_below(&AddSelectionBelow, cx);
8283 assert_eq!(
8284 view.selections.display_ranges(cx),
8285 vec![
8286 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
8287 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
8288 ]
8289 );
8290 });
8291
8292 view.update(cx, |view, cx| {
8293 view.add_selection_above(&AddSelectionAbove, cx);
8294 assert_eq!(
8295 view.selections.display_ranges(cx),
8296 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
8297 );
8298 });
8299
8300 view.update(cx, |view, cx| {
8301 view.add_selection_above(&AddSelectionAbove, cx);
8302 assert_eq!(
8303 view.selections.display_ranges(cx),
8304 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
8305 );
8306 });
8307
8308 view.update(cx, |view, cx| {
8309 view.change_selections(None, cx, |s| {
8310 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)])
8311 });
8312 view.add_selection_below(&AddSelectionBelow, cx);
8313 assert_eq!(
8314 view.selections.display_ranges(cx),
8315 vec![
8316 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8317 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8318 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8319 ]
8320 );
8321 });
8322
8323 view.update(cx, |view, cx| {
8324 view.add_selection_below(&AddSelectionBelow, cx);
8325 assert_eq!(
8326 view.selections.display_ranges(cx),
8327 vec![
8328 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8329 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8330 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8331 DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
8332 ]
8333 );
8334 });
8335
8336 view.update(cx, |view, cx| {
8337 view.add_selection_above(&AddSelectionAbove, cx);
8338 assert_eq!(
8339 view.selections.display_ranges(cx),
8340 vec![
8341 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8342 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8343 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8344 ]
8345 );
8346 });
8347
8348 view.update(cx, |view, cx| {
8349 view.change_selections(None, cx, |s| {
8350 s.select_display_ranges([DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)])
8351 });
8352 });
8353 view.update(cx, |view, cx| {
8354 view.add_selection_above(&AddSelectionAbove, cx);
8355 assert_eq!(
8356 view.selections.display_ranges(cx),
8357 vec![
8358 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
8359 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
8360 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
8361 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
8362 ]
8363 );
8364 });
8365
8366 view.update(cx, |view, cx| {
8367 view.add_selection_below(&AddSelectionBelow, cx);
8368 assert_eq!(
8369 view.selections.display_ranges(cx),
8370 vec![
8371 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
8372 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
8373 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
8374 ]
8375 );
8376 });
8377 }
8378
8379 #[gpui::test]
8380 fn test_select_next(cx: &mut gpui::MutableAppContext) {
8381 cx.set_global(Settings::test(cx));
8382
8383 let (text, ranges) = marked_text_ranges("[abc]\n[abc] [abc]\ndefabc\n[abc]");
8384 let buffer = MultiBuffer::build_simple(&text, cx);
8385 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8386
8387 view.update(cx, |view, cx| {
8388 view.change_selections(None, cx, |s| {
8389 s.select_ranges([ranges[1].start + 1..ranges[1].start + 1])
8390 });
8391 view.select_next(
8392 &SelectNext {
8393 replace_newest: false,
8394 },
8395 cx,
8396 );
8397 assert_eq!(view.selections.ranges(cx), &ranges[1..2]);
8398
8399 view.select_next(
8400 &SelectNext {
8401 replace_newest: false,
8402 },
8403 cx,
8404 );
8405 assert_eq!(view.selections.ranges(cx), &ranges[1..3]);
8406
8407 view.undo_selection(&UndoSelection, cx);
8408 assert_eq!(view.selections.ranges(cx), &ranges[1..2]);
8409
8410 view.redo_selection(&RedoSelection, cx);
8411 assert_eq!(view.selections.ranges(cx), &ranges[1..3]);
8412
8413 view.select_next(
8414 &SelectNext {
8415 replace_newest: false,
8416 },
8417 cx,
8418 );
8419 assert_eq!(view.selections.ranges(cx), &ranges[1..4]);
8420
8421 view.select_next(
8422 &SelectNext {
8423 replace_newest: false,
8424 },
8425 cx,
8426 );
8427 assert_eq!(view.selections.ranges(cx), &ranges[0..4]);
8428 });
8429 }
8430
8431 #[gpui::test]
8432 async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
8433 cx.update(|cx| cx.set_global(Settings::test(cx)));
8434 let language = Arc::new(Language::new(
8435 LanguageConfig::default(),
8436 Some(tree_sitter_rust::language()),
8437 ));
8438
8439 let text = r#"
8440 use mod1::mod2::{mod3, mod4};
8441
8442 fn fn_1(param1: bool, param2: &str) {
8443 let var1 = "text";
8444 }
8445 "#
8446 .unindent();
8447
8448 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8449 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8450 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
8451 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
8452 .await;
8453
8454 view.update(cx, |view, cx| {
8455 view.change_selections(None, cx, |s| {
8456 s.select_display_ranges([
8457 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8458 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8459 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8460 ]);
8461 });
8462 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8463 });
8464 assert_eq!(
8465 view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
8466 &[
8467 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
8468 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8469 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
8470 ]
8471 );
8472
8473 view.update(cx, |view, cx| {
8474 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8475 });
8476 assert_eq!(
8477 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8478 &[
8479 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
8480 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
8481 ]
8482 );
8483
8484 view.update(cx, |view, cx| {
8485 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8486 });
8487 assert_eq!(
8488 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8489 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
8490 );
8491
8492 // Trying to expand the selected syntax node one more time has no effect.
8493 view.update(cx, |view, cx| {
8494 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8495 });
8496 assert_eq!(
8497 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8498 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
8499 );
8500
8501 view.update(cx, |view, cx| {
8502 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8503 });
8504 assert_eq!(
8505 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8506 &[
8507 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
8508 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
8509 ]
8510 );
8511
8512 view.update(cx, |view, cx| {
8513 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8514 });
8515 assert_eq!(
8516 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8517 &[
8518 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
8519 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8520 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
8521 ]
8522 );
8523
8524 view.update(cx, |view, cx| {
8525 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8526 });
8527 assert_eq!(
8528 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8529 &[
8530 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8531 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8532 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8533 ]
8534 );
8535
8536 // Trying to shrink the selected syntax node one more time has no effect.
8537 view.update(cx, |view, cx| {
8538 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8539 });
8540 assert_eq!(
8541 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8542 &[
8543 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8544 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8545 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8546 ]
8547 );
8548
8549 // Ensure that we keep expanding the selection if the larger selection starts or ends within
8550 // a fold.
8551 view.update(cx, |view, cx| {
8552 view.fold_ranges(
8553 vec![
8554 Point::new(0, 21)..Point::new(0, 24),
8555 Point::new(3, 20)..Point::new(3, 22),
8556 ],
8557 cx,
8558 );
8559 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8560 });
8561 assert_eq!(
8562 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8563 &[
8564 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
8565 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8566 DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
8567 ]
8568 );
8569 }
8570
8571 #[gpui::test]
8572 async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
8573 cx.update(|cx| cx.set_global(Settings::test(cx)));
8574 let language = Arc::new(
8575 Language::new(
8576 LanguageConfig {
8577 brackets: vec![
8578 BracketPair {
8579 start: "{".to_string(),
8580 end: "}".to_string(),
8581 close: false,
8582 newline: true,
8583 },
8584 BracketPair {
8585 start: "(".to_string(),
8586 end: ")".to_string(),
8587 close: false,
8588 newline: true,
8589 },
8590 ],
8591 ..Default::default()
8592 },
8593 Some(tree_sitter_rust::language()),
8594 )
8595 .with_indents_query(
8596 r#"
8597 (_ "(" ")" @end) @indent
8598 (_ "{" "}" @end) @indent
8599 "#,
8600 )
8601 .unwrap(),
8602 );
8603
8604 let text = "fn a() {}";
8605
8606 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8607 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8608 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
8609 editor
8610 .condition(&cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
8611 .await;
8612
8613 editor.update(cx, |editor, cx| {
8614 editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
8615 editor.newline(&Newline, cx);
8616 assert_eq!(editor.text(cx), "fn a(\n \n) {\n \n}\n");
8617 assert_eq!(
8618 editor.selections.ranges(cx),
8619 &[
8620 Point::new(1, 4)..Point::new(1, 4),
8621 Point::new(3, 4)..Point::new(3, 4),
8622 Point::new(5, 0)..Point::new(5, 0)
8623 ]
8624 );
8625 });
8626 }
8627
8628 #[gpui::test]
8629 async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
8630 cx.update(|cx| cx.set_global(Settings::test(cx)));
8631 let language = Arc::new(Language::new(
8632 LanguageConfig {
8633 brackets: vec![
8634 BracketPair {
8635 start: "{".to_string(),
8636 end: "}".to_string(),
8637 close: true,
8638 newline: true,
8639 },
8640 BracketPair {
8641 start: "/*".to_string(),
8642 end: " */".to_string(),
8643 close: true,
8644 newline: true,
8645 },
8646 ],
8647 autoclose_before: "})]".to_string(),
8648 ..Default::default()
8649 },
8650 Some(tree_sitter_rust::language()),
8651 ));
8652
8653 let text = r#"
8654 a
8655
8656 /
8657
8658 "#
8659 .unindent();
8660
8661 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8662 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8663 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
8664 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
8665 .await;
8666
8667 view.update(cx, |view, cx| {
8668 view.change_selections(None, cx, |s| {
8669 s.select_display_ranges([
8670 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8671 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8672 ])
8673 });
8674
8675 view.handle_input(&Input("{".to_string()), cx);
8676 view.handle_input(&Input("{".to_string()), cx);
8677 view.handle_input(&Input("{".to_string()), cx);
8678 assert_eq!(
8679 view.text(cx),
8680 "
8681 {{{}}}
8682 {{{}}}
8683 /
8684
8685 "
8686 .unindent()
8687 );
8688
8689 view.move_right(&MoveRight, cx);
8690 view.handle_input(&Input("}".to_string()), cx);
8691 view.handle_input(&Input("}".to_string()), cx);
8692 view.handle_input(&Input("}".to_string()), cx);
8693 assert_eq!(
8694 view.text(cx),
8695 "
8696 {{{}}}}
8697 {{{}}}}
8698 /
8699
8700 "
8701 .unindent()
8702 );
8703
8704 view.undo(&Undo, cx);
8705 view.handle_input(&Input("/".to_string()), cx);
8706 view.handle_input(&Input("*".to_string()), cx);
8707 assert_eq!(
8708 view.text(cx),
8709 "
8710 /* */
8711 /* */
8712 /
8713
8714 "
8715 .unindent()
8716 );
8717
8718 view.undo(&Undo, cx);
8719 view.change_selections(None, cx, |s| {
8720 s.select_display_ranges([
8721 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8722 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8723 ])
8724 });
8725 view.handle_input(&Input("*".to_string()), cx);
8726 assert_eq!(
8727 view.text(cx),
8728 "
8729 a
8730
8731 /*
8732 *
8733 "
8734 .unindent()
8735 );
8736
8737 // Don't autoclose if the next character isn't whitespace and isn't
8738 // listed in the language's "autoclose_before" section.
8739 view.finalize_last_transaction(cx);
8740 view.change_selections(None, cx, |s| {
8741 s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)])
8742 });
8743 view.handle_input(&Input("{".to_string()), cx);
8744 assert_eq!(
8745 view.text(cx),
8746 "
8747 {a
8748
8749 /*
8750 *
8751 "
8752 .unindent()
8753 );
8754
8755 view.undo(&Undo, cx);
8756 view.change_selections(None, cx, |s| {
8757 s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1)])
8758 });
8759 view.handle_input(&Input("{".to_string()), cx);
8760 assert_eq!(
8761 view.text(cx),
8762 "
8763 {a}
8764
8765 /*
8766 *
8767 "
8768 .unindent()
8769 );
8770 assert_eq!(
8771 view.selections.display_ranges(cx),
8772 [DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]
8773 );
8774 });
8775 }
8776
8777 #[gpui::test]
8778 async fn test_snippets(cx: &mut gpui::TestAppContext) {
8779 cx.update(|cx| cx.set_global(Settings::test(cx)));
8780
8781 let (text, insertion_ranges) = marked_text_ranges(indoc! {"
8782 a.| b
8783 a.| b
8784 a.| b"});
8785 let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
8786 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
8787
8788 editor.update(cx, |editor, cx| {
8789 let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
8790
8791 editor
8792 .insert_snippet(&insertion_ranges, snippet, cx)
8793 .unwrap();
8794
8795 fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text_ranges: &str) {
8796 let range_markers = ('<', '>');
8797 let (expected_text, mut selection_ranges_lookup) =
8798 marked_text_ranges_by(marked_text_ranges, vec![range_markers.clone().into()]);
8799 let selection_ranges = selection_ranges_lookup
8800 .remove(&range_markers.into())
8801 .unwrap();
8802 assert_eq!(editor.text(cx), expected_text);
8803 assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
8804 }
8805 assert(
8806 editor,
8807 cx,
8808 indoc! {"
8809 a.f(<one>, two, <three>) b
8810 a.f(<one>, two, <three>) b
8811 a.f(<one>, two, <three>) b"},
8812 );
8813
8814 // Can't move earlier than the first tab stop
8815 assert!(!editor.move_to_prev_snippet_tabstop(cx));
8816 assert(
8817 editor,
8818 cx,
8819 indoc! {"
8820 a.f(<one>, two, <three>) b
8821 a.f(<one>, two, <three>) b
8822 a.f(<one>, two, <three>) b"},
8823 );
8824
8825 assert!(editor.move_to_next_snippet_tabstop(cx));
8826 assert(
8827 editor,
8828 cx,
8829 indoc! {"
8830 a.f(one, <two>, three) b
8831 a.f(one, <two>, three) b
8832 a.f(one, <two>, three) b"},
8833 );
8834
8835 editor.move_to_prev_snippet_tabstop(cx);
8836 assert(
8837 editor,
8838 cx,
8839 indoc! {"
8840 a.f(<one>, two, <three>) b
8841 a.f(<one>, two, <three>) b
8842 a.f(<one>, two, <three>) b"},
8843 );
8844
8845 assert!(editor.move_to_next_snippet_tabstop(cx));
8846 assert(
8847 editor,
8848 cx,
8849 indoc! {"
8850 a.f(one, <two>, three) b
8851 a.f(one, <two>, three) b
8852 a.f(one, <two>, three) b"},
8853 );
8854 assert!(editor.move_to_next_snippet_tabstop(cx));
8855 assert(
8856 editor,
8857 cx,
8858 indoc! {"
8859 a.f(one, two, three)<> b
8860 a.f(one, two, three)<> b
8861 a.f(one, two, three)<> b"},
8862 );
8863
8864 // As soon as the last tab stop is reached, snippet state is gone
8865 editor.move_to_prev_snippet_tabstop(cx);
8866 assert(
8867 editor,
8868 cx,
8869 indoc! {"
8870 a.f(one, two, three)<> b
8871 a.f(one, two, three)<> b
8872 a.f(one, two, three)<> b"},
8873 );
8874 });
8875 }
8876
8877 #[gpui::test]
8878 async fn test_format_during_save(cx: &mut gpui::TestAppContext) {
8879 cx.foreground().forbid_parking();
8880 cx.update(|cx| cx.set_global(Settings::test(cx)));
8881
8882 let mut language = Language::new(
8883 LanguageConfig {
8884 name: "Rust".into(),
8885 path_suffixes: vec!["rs".to_string()],
8886 ..Default::default()
8887 },
8888 Some(tree_sitter_rust::language()),
8889 );
8890 let mut fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
8891 capabilities: lsp::ServerCapabilities {
8892 document_formatting_provider: Some(lsp::OneOf::Left(true)),
8893 ..Default::default()
8894 },
8895 ..Default::default()
8896 });
8897
8898 let fs = FakeFs::new(cx.background().clone());
8899 fs.insert_file("/file.rs", Default::default()).await;
8900
8901 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
8902 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
8903 let buffer = project
8904 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
8905 .await
8906 .unwrap();
8907
8908 cx.foreground().start_waiting();
8909 let fake_server = fake_servers.next().await.unwrap();
8910
8911 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8912 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
8913 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
8914 assert!(cx.read(|cx| editor.is_dirty(cx)));
8915
8916 let save = cx.update(|cx| editor.save(project.clone(), cx));
8917 fake_server
8918 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
8919 assert_eq!(
8920 params.text_document.uri,
8921 lsp::Url::from_file_path("/file.rs").unwrap()
8922 );
8923 assert_eq!(params.options.tab_size, 4);
8924 Ok(Some(vec![lsp::TextEdit::new(
8925 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
8926 ", ".to_string(),
8927 )]))
8928 })
8929 .next()
8930 .await;
8931 cx.foreground().start_waiting();
8932 save.await.unwrap();
8933 assert_eq!(
8934 editor.read_with(cx, |editor, cx| editor.text(cx)),
8935 "one, two\nthree\n"
8936 );
8937 assert!(!cx.read(|cx| editor.is_dirty(cx)));
8938
8939 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
8940 assert!(cx.read(|cx| editor.is_dirty(cx)));
8941
8942 // Ensure we can still save even if formatting hangs.
8943 fake_server.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 futures::future::pending::<()>().await;
8949 unreachable!()
8950 });
8951 let save = cx.update(|cx| editor.save(project.clone(), cx));
8952 cx.foreground().advance_clock(items::FORMAT_TIMEOUT);
8953 cx.foreground().start_waiting();
8954 save.await.unwrap();
8955 assert_eq!(
8956 editor.read_with(cx, |editor, cx| editor.text(cx)),
8957 "one\ntwo\nthree\n"
8958 );
8959 assert!(!cx.read(|cx| editor.is_dirty(cx)));
8960
8961 // Set rust language override and assert overriden tabsize is sent to language server
8962 cx.update(|cx| {
8963 cx.update_global::<Settings, _, _>(|settings, _| {
8964 settings.language_overrides.insert(
8965 "Rust".into(),
8966 LanguageOverride {
8967 tab_size: Some(8),
8968 ..Default::default()
8969 },
8970 );
8971 })
8972 });
8973
8974 let save = cx.update(|cx| editor.save(project.clone(), cx));
8975 fake_server
8976 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
8977 assert_eq!(
8978 params.text_document.uri,
8979 lsp::Url::from_file_path("/file.rs").unwrap()
8980 );
8981 assert_eq!(params.options.tab_size, 8);
8982 Ok(Some(vec![]))
8983 })
8984 .next()
8985 .await;
8986 cx.foreground().start_waiting();
8987 save.await.unwrap();
8988 }
8989
8990 #[gpui::test]
8991 async fn test_completion(cx: &mut gpui::TestAppContext) {
8992 cx.update(|cx| cx.set_global(Settings::test(cx)));
8993
8994 let mut language = Language::new(
8995 LanguageConfig {
8996 name: "Rust".into(),
8997 path_suffixes: vec!["rs".to_string()],
8998 ..Default::default()
8999 },
9000 Some(tree_sitter_rust::language()),
9001 );
9002 let mut fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
9003 capabilities: lsp::ServerCapabilities {
9004 completion_provider: Some(lsp::CompletionOptions {
9005 trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
9006 ..Default::default()
9007 }),
9008 ..Default::default()
9009 },
9010 ..Default::default()
9011 });
9012
9013 let text = "
9014 one
9015 two
9016 three
9017 "
9018 .unindent();
9019
9020 let fs = FakeFs::new(cx.background().clone());
9021 fs.insert_file("/file.rs", text).await;
9022
9023 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
9024 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
9025 let buffer = project
9026 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
9027 .await
9028 .unwrap();
9029 let mut fake_server = fake_servers.next().await.unwrap();
9030
9031 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9032 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9033
9034 editor.update(cx, |editor, cx| {
9035 editor.project = Some(project);
9036 editor.change_selections(None, cx, |s| {
9037 s.select_ranges([Point::new(0, 3)..Point::new(0, 3)])
9038 });
9039 editor.handle_input(&Input(".".to_string()), cx);
9040 });
9041
9042 handle_completion_request(
9043 &mut fake_server,
9044 "/file.rs",
9045 Point::new(0, 4),
9046 vec![
9047 (Point::new(0, 4)..Point::new(0, 4), "first_completion"),
9048 (Point::new(0, 4)..Point::new(0, 4), "second_completion"),
9049 ],
9050 )
9051 .await;
9052 editor
9053 .condition(&cx, |editor, _| editor.context_menu_visible())
9054 .await;
9055
9056 let apply_additional_edits = editor.update(cx, |editor, cx| {
9057 editor.move_down(&MoveDown, cx);
9058 let apply_additional_edits = editor
9059 .confirm_completion(&ConfirmCompletion::default(), cx)
9060 .unwrap();
9061 assert_eq!(
9062 editor.text(cx),
9063 "
9064 one.second_completion
9065 two
9066 three
9067 "
9068 .unindent()
9069 );
9070 apply_additional_edits
9071 });
9072
9073 handle_resolve_completion_request(
9074 &mut fake_server,
9075 Some((Point::new(2, 5)..Point::new(2, 5), "\nadditional edit")),
9076 )
9077 .await;
9078 apply_additional_edits.await.unwrap();
9079 assert_eq!(
9080 editor.read_with(cx, |editor, cx| editor.text(cx)),
9081 "
9082 one.second_completion
9083 two
9084 three
9085 additional edit
9086 "
9087 .unindent()
9088 );
9089
9090 editor.update(cx, |editor, cx| {
9091 editor.change_selections(None, cx, |s| {
9092 s.select_ranges([
9093 Point::new(1, 3)..Point::new(1, 3),
9094 Point::new(2, 5)..Point::new(2, 5),
9095 ])
9096 });
9097
9098 editor.handle_input(&Input(" ".to_string()), cx);
9099 assert!(editor.context_menu.is_none());
9100 editor.handle_input(&Input("s".to_string()), cx);
9101 assert!(editor.context_menu.is_none());
9102 });
9103
9104 handle_completion_request(
9105 &mut fake_server,
9106 "/file.rs",
9107 Point::new(2, 7),
9108 vec![
9109 (Point::new(2, 6)..Point::new(2, 7), "fourth_completion"),
9110 (Point::new(2, 6)..Point::new(2, 7), "fifth_completion"),
9111 (Point::new(2, 6)..Point::new(2, 7), "sixth_completion"),
9112 ],
9113 )
9114 .await;
9115 editor
9116 .condition(&cx, |editor, _| editor.context_menu_visible())
9117 .await;
9118
9119 editor.update(cx, |editor, cx| {
9120 editor.handle_input(&Input("i".to_string()), cx);
9121 });
9122
9123 handle_completion_request(
9124 &mut fake_server,
9125 "/file.rs",
9126 Point::new(2, 8),
9127 vec![
9128 (Point::new(2, 6)..Point::new(2, 8), "fourth_completion"),
9129 (Point::new(2, 6)..Point::new(2, 8), "fifth_completion"),
9130 (Point::new(2, 6)..Point::new(2, 8), "sixth_completion"),
9131 ],
9132 )
9133 .await;
9134 editor
9135 .condition(&cx, |editor, _| editor.context_menu_visible())
9136 .await;
9137
9138 let apply_additional_edits = editor.update(cx, |editor, cx| {
9139 let apply_additional_edits = editor
9140 .confirm_completion(&ConfirmCompletion::default(), cx)
9141 .unwrap();
9142 assert_eq!(
9143 editor.text(cx),
9144 "
9145 one.second_completion
9146 two sixth_completion
9147 three sixth_completion
9148 additional edit
9149 "
9150 .unindent()
9151 );
9152 apply_additional_edits
9153 });
9154 handle_resolve_completion_request(&mut fake_server, None).await;
9155 apply_additional_edits.await.unwrap();
9156
9157 async fn handle_completion_request(
9158 fake: &mut FakeLanguageServer,
9159 path: &'static str,
9160 position: Point,
9161 completions: Vec<(Range<Point>, &'static str)>,
9162 ) {
9163 fake.handle_request::<lsp::request::Completion, _, _>(move |params, _| {
9164 let completions = completions.clone();
9165 async move {
9166 assert_eq!(
9167 params.text_document_position.text_document.uri,
9168 lsp::Url::from_file_path(path).unwrap()
9169 );
9170 assert_eq!(
9171 params.text_document_position.position,
9172 lsp::Position::new(position.row, position.column)
9173 );
9174 Ok(Some(lsp::CompletionResponse::Array(
9175 completions
9176 .iter()
9177 .map(|(range, new_text)| lsp::CompletionItem {
9178 label: new_text.to_string(),
9179 text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
9180 range: lsp::Range::new(
9181 lsp::Position::new(range.start.row, range.start.column),
9182 lsp::Position::new(range.start.row, range.start.column),
9183 ),
9184 new_text: new_text.to_string(),
9185 })),
9186 ..Default::default()
9187 })
9188 .collect(),
9189 )))
9190 }
9191 })
9192 .next()
9193 .await;
9194 }
9195
9196 async fn handle_resolve_completion_request(
9197 fake: &mut FakeLanguageServer,
9198 edit: Option<(Range<Point>, &'static str)>,
9199 ) {
9200 fake.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _| {
9201 let edit = edit.clone();
9202 async move {
9203 Ok(lsp::CompletionItem {
9204 additional_text_edits: edit.map(|(range, new_text)| {
9205 vec![lsp::TextEdit::new(
9206 lsp::Range::new(
9207 lsp::Position::new(range.start.row, range.start.column),
9208 lsp::Position::new(range.end.row, range.end.column),
9209 ),
9210 new_text.to_string(),
9211 )]
9212 }),
9213 ..Default::default()
9214 })
9215 }
9216 })
9217 .next()
9218 .await;
9219 }
9220 }
9221
9222 #[gpui::test]
9223 async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
9224 cx.update(|cx| cx.set_global(Settings::test(cx)));
9225 let language = Arc::new(Language::new(
9226 LanguageConfig {
9227 line_comment: Some("// ".to_string()),
9228 ..Default::default()
9229 },
9230 Some(tree_sitter_rust::language()),
9231 ));
9232
9233 let text = "
9234 fn a() {
9235 //b();
9236 // c();
9237 // d();
9238 }
9239 "
9240 .unindent();
9241
9242 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9243 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9244 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9245
9246 view.update(cx, |editor, cx| {
9247 // If multiple selections intersect a line, the line is only
9248 // toggled once.
9249 editor.change_selections(None, cx, |s| {
9250 s.select_display_ranges([
9251 DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3),
9252 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6),
9253 ])
9254 });
9255 editor.toggle_comments(&ToggleComments, cx);
9256 assert_eq!(
9257 editor.text(cx),
9258 "
9259 fn a() {
9260 b();
9261 c();
9262 d();
9263 }
9264 "
9265 .unindent()
9266 );
9267
9268 // The comment prefix is inserted at the same column for every line
9269 // in a selection.
9270 editor.change_selections(None, cx, |s| {
9271 s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)])
9272 });
9273 editor.toggle_comments(&ToggleComments, cx);
9274 assert_eq!(
9275 editor.text(cx),
9276 "
9277 fn a() {
9278 // b();
9279 // c();
9280 // d();
9281 }
9282 "
9283 .unindent()
9284 );
9285
9286 // If a selection ends at the beginning of a line, that line is not toggled.
9287 editor.change_selections(None, cx, |s| {
9288 s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)])
9289 });
9290 editor.toggle_comments(&ToggleComments, cx);
9291 assert_eq!(
9292 editor.text(cx),
9293 "
9294 fn a() {
9295 // b();
9296 c();
9297 // d();
9298 }
9299 "
9300 .unindent()
9301 );
9302 });
9303 }
9304
9305 #[gpui::test]
9306 fn test_editing_disjoint_excerpts(cx: &mut gpui::MutableAppContext) {
9307 cx.set_global(Settings::test(cx));
9308 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
9309 let multibuffer = cx.add_model(|cx| {
9310 let mut multibuffer = MultiBuffer::new(0);
9311 multibuffer.push_excerpts(
9312 buffer.clone(),
9313 [
9314 Point::new(0, 0)..Point::new(0, 4),
9315 Point::new(1, 0)..Point::new(1, 4),
9316 ],
9317 cx,
9318 );
9319 multibuffer
9320 });
9321
9322 assert_eq!(multibuffer.read(cx).read(cx).text(), "aaaa\nbbbb");
9323
9324 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
9325 view.update(cx, |view, cx| {
9326 assert_eq!(view.text(cx), "aaaa\nbbbb");
9327 view.change_selections(None, cx, |s| {
9328 s.select_ranges([
9329 Point::new(0, 0)..Point::new(0, 0),
9330 Point::new(1, 0)..Point::new(1, 0),
9331 ])
9332 });
9333
9334 view.handle_input(&Input("X".to_string()), cx);
9335 assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
9336 assert_eq!(
9337 view.selections.ranges(cx),
9338 [
9339 Point::new(0, 1)..Point::new(0, 1),
9340 Point::new(1, 1)..Point::new(1, 1),
9341 ]
9342 )
9343 });
9344 }
9345
9346 #[gpui::test]
9347 fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) {
9348 cx.set_global(Settings::test(cx));
9349 let (initial_text, excerpt_ranges) = marked_text_ranges(indoc! {"
9350 [aaaa
9351 (bbbb]
9352 cccc)"});
9353 let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx));
9354 let multibuffer = cx.add_model(|cx| {
9355 let mut multibuffer = MultiBuffer::new(0);
9356 multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
9357 multibuffer
9358 });
9359
9360 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
9361 view.update(cx, |view, cx| {
9362 let (expected_text, selection_ranges) = marked_text_ranges(indoc! {"
9363 aaaa
9364 b|bbb
9365 b|bb|b
9366 cccc"});
9367 assert_eq!(view.text(cx), expected_text);
9368 view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
9369
9370 view.handle_input(&Input("X".to_string()), cx);
9371
9372 let (expected_text, expected_selections) = marked_text_ranges(indoc! {"
9373 aaaa
9374 bX|bbXb
9375 bX|bbX|b
9376 cccc"});
9377 assert_eq!(view.text(cx), expected_text);
9378 assert_eq!(view.selections.ranges(cx), expected_selections);
9379
9380 view.newline(&Newline, cx);
9381 let (expected_text, expected_selections) = marked_text_ranges(indoc! {"
9382 aaaa
9383 bX
9384 |bbX
9385 b
9386 bX
9387 |bbX
9388 |b
9389 cccc"});
9390 assert_eq!(view.text(cx), expected_text);
9391 assert_eq!(view.selections.ranges(cx), expected_selections);
9392 });
9393 }
9394
9395 #[gpui::test]
9396 fn test_refresh_selections(cx: &mut gpui::MutableAppContext) {
9397 cx.set_global(Settings::test(cx));
9398 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
9399 let mut excerpt1_id = None;
9400 let multibuffer = cx.add_model(|cx| {
9401 let mut multibuffer = MultiBuffer::new(0);
9402 excerpt1_id = multibuffer
9403 .push_excerpts(
9404 buffer.clone(),
9405 [
9406 Point::new(0, 0)..Point::new(1, 4),
9407 Point::new(1, 0)..Point::new(2, 4),
9408 ],
9409 cx,
9410 )
9411 .into_iter()
9412 .next();
9413 multibuffer
9414 });
9415 assert_eq!(
9416 multibuffer.read(cx).read(cx).text(),
9417 "aaaa\nbbbb\nbbbb\ncccc"
9418 );
9419 let (_, editor) = cx.add_window(Default::default(), |cx| {
9420 let mut editor = build_editor(multibuffer.clone(), cx);
9421 let snapshot = editor.snapshot(cx);
9422 editor.change_selections(None, cx, |s| {
9423 s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
9424 });
9425 editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
9426 assert_eq!(
9427 editor.selections.ranges(cx),
9428 [
9429 Point::new(1, 3)..Point::new(1, 3),
9430 Point::new(2, 1)..Point::new(2, 1),
9431 ]
9432 );
9433 editor
9434 });
9435
9436 // Refreshing selections is a no-op when excerpts haven't changed.
9437 editor.update(cx, |editor, cx| {
9438 editor.change_selections(None, cx, |s| {
9439 s.refresh();
9440 });
9441 assert_eq!(
9442 editor.selections.ranges(cx),
9443 [
9444 Point::new(1, 3)..Point::new(1, 3),
9445 Point::new(2, 1)..Point::new(2, 1),
9446 ]
9447 );
9448 });
9449
9450 multibuffer.update(cx, |multibuffer, cx| {
9451 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
9452 });
9453 editor.update(cx, |editor, cx| {
9454 // Removing an excerpt causes the first selection to become degenerate.
9455 assert_eq!(
9456 editor.selections.ranges(cx),
9457 [
9458 Point::new(0, 0)..Point::new(0, 0),
9459 Point::new(0, 1)..Point::new(0, 1)
9460 ]
9461 );
9462
9463 // Refreshing selections will relocate the first selection to the original buffer
9464 // location.
9465 editor.change_selections(None, cx, |s| {
9466 s.refresh();
9467 });
9468 assert_eq!(
9469 editor.selections.ranges(cx),
9470 [
9471 Point::new(0, 1)..Point::new(0, 1),
9472 Point::new(0, 3)..Point::new(0, 3)
9473 ]
9474 );
9475 assert!(editor.selections.pending_anchor().is_some());
9476 });
9477 }
9478
9479 #[gpui::test]
9480 fn test_refresh_selections_while_selecting_with_mouse(cx: &mut gpui::MutableAppContext) {
9481 cx.set_global(Settings::test(cx));
9482 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
9483 let mut excerpt1_id = None;
9484 let multibuffer = cx.add_model(|cx| {
9485 let mut multibuffer = MultiBuffer::new(0);
9486 excerpt1_id = multibuffer
9487 .push_excerpts(
9488 buffer.clone(),
9489 [
9490 Point::new(0, 0)..Point::new(1, 4),
9491 Point::new(1, 0)..Point::new(2, 4),
9492 ],
9493 cx,
9494 )
9495 .into_iter()
9496 .next();
9497 multibuffer
9498 });
9499 assert_eq!(
9500 multibuffer.read(cx).read(cx).text(),
9501 "aaaa\nbbbb\nbbbb\ncccc"
9502 );
9503 let (_, editor) = cx.add_window(Default::default(), |cx| {
9504 let mut editor = build_editor(multibuffer.clone(), cx);
9505 let snapshot = editor.snapshot(cx);
9506 editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
9507 assert_eq!(
9508 editor.selections.ranges(cx),
9509 [Point::new(1, 3)..Point::new(1, 3)]
9510 );
9511 editor
9512 });
9513
9514 multibuffer.update(cx, |multibuffer, cx| {
9515 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
9516 });
9517 editor.update(cx, |editor, cx| {
9518 assert_eq!(
9519 editor.selections.ranges(cx),
9520 [Point::new(0, 0)..Point::new(0, 0)]
9521 );
9522
9523 // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
9524 editor.change_selections(None, cx, |s| {
9525 s.refresh();
9526 });
9527 assert_eq!(
9528 editor.selections.ranges(cx),
9529 [Point::new(0, 3)..Point::new(0, 3)]
9530 );
9531 assert!(editor.selections.pending_anchor().is_some());
9532 });
9533 }
9534
9535 #[gpui::test]
9536 async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
9537 cx.update(|cx| cx.set_global(Settings::test(cx)));
9538 let language = Arc::new(Language::new(
9539 LanguageConfig {
9540 brackets: vec![
9541 BracketPair {
9542 start: "{".to_string(),
9543 end: "}".to_string(),
9544 close: true,
9545 newline: true,
9546 },
9547 BracketPair {
9548 start: "/* ".to_string(),
9549 end: " */".to_string(),
9550 close: true,
9551 newline: true,
9552 },
9553 ],
9554 ..Default::default()
9555 },
9556 Some(tree_sitter_rust::language()),
9557 ));
9558
9559 let text = concat!(
9560 "{ }\n", // Suppress rustfmt
9561 " x\n", //
9562 " /* */\n", //
9563 "x\n", //
9564 "{{} }\n", //
9565 );
9566
9567 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9568 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9569 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9570 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9571 .await;
9572
9573 view.update(cx, |view, cx| {
9574 view.change_selections(None, cx, |s| {
9575 s.select_display_ranges([
9576 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
9577 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
9578 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
9579 ])
9580 });
9581 view.newline(&Newline, cx);
9582
9583 assert_eq!(
9584 view.buffer().read(cx).read(cx).text(),
9585 concat!(
9586 "{ \n", // Suppress rustfmt
9587 "\n", //
9588 "}\n", //
9589 " x\n", //
9590 " /* \n", //
9591 " \n", //
9592 " */\n", //
9593 "x\n", //
9594 "{{} \n", //
9595 "}\n", //
9596 )
9597 );
9598 });
9599 }
9600
9601 #[gpui::test]
9602 fn test_highlighted_ranges(cx: &mut gpui::MutableAppContext) {
9603 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
9604
9605 cx.set_global(Settings::test(cx));
9606 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
9607
9608 editor.update(cx, |editor, cx| {
9609 struct Type1;
9610 struct Type2;
9611
9612 let buffer = buffer.read(cx).snapshot(cx);
9613
9614 let anchor_range = |range: Range<Point>| {
9615 buffer.anchor_after(range.start)..buffer.anchor_after(range.end)
9616 };
9617
9618 editor.highlight_background::<Type1>(
9619 vec![
9620 anchor_range(Point::new(2, 1)..Point::new(2, 3)),
9621 anchor_range(Point::new(4, 2)..Point::new(4, 4)),
9622 anchor_range(Point::new(6, 3)..Point::new(6, 5)),
9623 anchor_range(Point::new(8, 4)..Point::new(8, 6)),
9624 ],
9625 |_| Color::red(),
9626 cx,
9627 );
9628 editor.highlight_background::<Type2>(
9629 vec![
9630 anchor_range(Point::new(3, 2)..Point::new(3, 5)),
9631 anchor_range(Point::new(5, 3)..Point::new(5, 6)),
9632 anchor_range(Point::new(7, 4)..Point::new(7, 7)),
9633 anchor_range(Point::new(9, 5)..Point::new(9, 8)),
9634 ],
9635 |_| Color::green(),
9636 cx,
9637 );
9638
9639 let snapshot = editor.snapshot(cx);
9640 let mut highlighted_ranges = editor.background_highlights_in_range(
9641 anchor_range(Point::new(3, 4)..Point::new(7, 4)),
9642 &snapshot,
9643 cx.global::<Settings>().theme.as_ref(),
9644 );
9645 // Enforce a consistent ordering based on color without relying on the ordering of the
9646 // highlight's `TypeId` which is non-deterministic.
9647 highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
9648 assert_eq!(
9649 highlighted_ranges,
9650 &[
9651 (
9652 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
9653 Color::green(),
9654 ),
9655 (
9656 DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
9657 Color::green(),
9658 ),
9659 (
9660 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
9661 Color::red(),
9662 ),
9663 (
9664 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
9665 Color::red(),
9666 ),
9667 ]
9668 );
9669 assert_eq!(
9670 editor.background_highlights_in_range(
9671 anchor_range(Point::new(5, 6)..Point::new(6, 4)),
9672 &snapshot,
9673 cx.global::<Settings>().theme.as_ref(),
9674 ),
9675 &[(
9676 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
9677 Color::red(),
9678 )]
9679 );
9680 });
9681 }
9682
9683 #[gpui::test]
9684 fn test_following(cx: &mut gpui::MutableAppContext) {
9685 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
9686
9687 cx.set_global(Settings::test(cx));
9688
9689 let (_, leader) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
9690 let (_, follower) = cx.add_window(
9691 WindowOptions {
9692 bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
9693 ..Default::default()
9694 },
9695 |cx| build_editor(buffer.clone(), cx),
9696 );
9697
9698 let pending_update = Rc::new(RefCell::new(None));
9699 follower.update(cx, {
9700 let update = pending_update.clone();
9701 |_, cx| {
9702 cx.subscribe(&leader, move |_, leader, event, cx| {
9703 leader
9704 .read(cx)
9705 .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
9706 })
9707 .detach();
9708 }
9709 });
9710
9711 // Update the selections only
9712 leader.update(cx, |leader, cx| {
9713 leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
9714 });
9715 follower.update(cx, |follower, cx| {
9716 follower
9717 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
9718 .unwrap();
9719 });
9720 assert_eq!(follower.read(cx).selections.ranges(cx), vec![1..1]);
9721
9722 // Update the scroll position only
9723 leader.update(cx, |leader, cx| {
9724 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
9725 });
9726 follower.update(cx, |follower, cx| {
9727 follower
9728 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
9729 .unwrap();
9730 });
9731 assert_eq!(
9732 follower.update(cx, |follower, cx| follower.scroll_position(cx)),
9733 vec2f(1.5, 3.5)
9734 );
9735
9736 // Update the selections and scroll position
9737 leader.update(cx, |leader, cx| {
9738 leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
9739 leader.request_autoscroll(Autoscroll::Newest, cx);
9740 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
9741 });
9742 follower.update(cx, |follower, cx| {
9743 let initial_scroll_position = follower.scroll_position(cx);
9744 follower
9745 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
9746 .unwrap();
9747 assert_eq!(follower.scroll_position(cx), initial_scroll_position);
9748 assert!(follower.autoscroll_request.is_some());
9749 });
9750 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0]);
9751
9752 // Creating a pending selection that precedes another selection
9753 leader.update(cx, |leader, cx| {
9754 leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
9755 leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
9756 });
9757 follower.update(cx, |follower, cx| {
9758 follower
9759 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
9760 .unwrap();
9761 });
9762 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0, 1..1]);
9763
9764 // Extend the pending selection so that it surrounds another selection
9765 leader.update(cx, |leader, cx| {
9766 leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
9767 });
9768 follower.update(cx, |follower, cx| {
9769 follower
9770 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
9771 .unwrap();
9772 });
9773 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..2]);
9774 }
9775
9776 #[test]
9777 fn test_combine_syntax_and_fuzzy_match_highlights() {
9778 let string = "abcdefghijklmnop";
9779 let syntax_ranges = [
9780 (
9781 0..3,
9782 HighlightStyle {
9783 color: Some(Color::red()),
9784 ..Default::default()
9785 },
9786 ),
9787 (
9788 4..8,
9789 HighlightStyle {
9790 color: Some(Color::green()),
9791 ..Default::default()
9792 },
9793 ),
9794 ];
9795 let match_indices = [4, 6, 7, 8];
9796 assert_eq!(
9797 combine_syntax_and_fuzzy_match_highlights(
9798 &string,
9799 Default::default(),
9800 syntax_ranges.into_iter(),
9801 &match_indices,
9802 ),
9803 &[
9804 (
9805 0..3,
9806 HighlightStyle {
9807 color: Some(Color::red()),
9808 ..Default::default()
9809 },
9810 ),
9811 (
9812 4..5,
9813 HighlightStyle {
9814 color: Some(Color::green()),
9815 weight: Some(fonts::Weight::BOLD),
9816 ..Default::default()
9817 },
9818 ),
9819 (
9820 5..6,
9821 HighlightStyle {
9822 color: Some(Color::green()),
9823 ..Default::default()
9824 },
9825 ),
9826 (
9827 6..8,
9828 HighlightStyle {
9829 color: Some(Color::green()),
9830 weight: Some(fonts::Weight::BOLD),
9831 ..Default::default()
9832 },
9833 ),
9834 (
9835 8..9,
9836 HighlightStyle {
9837 weight: Some(fonts::Weight::BOLD),
9838 ..Default::default()
9839 },
9840 ),
9841 ]
9842 );
9843 }
9844
9845 fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
9846 let point = DisplayPoint::new(row as u32, column as u32);
9847 point..point
9848 }
9849
9850 fn assert_selection_ranges(
9851 marked_text: &str,
9852 selection_marker_pairs: Vec<(char, char)>,
9853 view: &mut Editor,
9854 cx: &mut ViewContext<Editor>,
9855 ) {
9856 let snapshot = view.snapshot(cx).display_snapshot;
9857 let mut marker_chars = Vec::new();
9858 for (start, end) in selection_marker_pairs.iter() {
9859 marker_chars.push(*start);
9860 marker_chars.push(*end);
9861 }
9862 let (_, markers) = marked_text_by(marked_text, marker_chars);
9863 let asserted_ranges: Vec<Range<DisplayPoint>> = selection_marker_pairs
9864 .iter()
9865 .map(|(start, end)| {
9866 let start = markers.get(start).unwrap()[0].to_display_point(&snapshot);
9867 let end = markers.get(end).unwrap()[0].to_display_point(&snapshot);
9868 start..end
9869 })
9870 .collect();
9871 assert_eq!(
9872 view.selections.display_ranges(cx),
9873 &asserted_ranges[..],
9874 "Assert selections are {}",
9875 marked_text
9876 );
9877 }
9878}
9879
9880trait RangeExt<T> {
9881 fn sorted(&self) -> Range<T>;
9882 fn to_inclusive(&self) -> RangeInclusive<T>;
9883}
9884
9885impl<T: Ord + Clone> RangeExt<T> for Range<T> {
9886 fn sorted(&self) -> Self {
9887 cmp::min(&self.start, &self.end).clone()..cmp::max(&self.start, &self.end).clone()
9888 }
9889
9890 fn to_inclusive(&self) -> RangeInclusive<T> {
9891 self.start.clone()..=self.end.clone()
9892 }
9893}