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