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