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