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