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