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