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 if 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 }
4671 return;
4672 }
4673 }
4674
4675 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
4676 active_diagnostics
4677 .primary_range
4678 .to_offset(&buffer)
4679 .to_inclusive()
4680 });
4681 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
4682 if active_primary_range.contains(&selection.head()) {
4683 *active_primary_range.end()
4684 } else {
4685 selection.head()
4686 }
4687 } else {
4688 selection.head()
4689 };
4690
4691 loop {
4692 let mut diagnostics = if direction == Direction::Prev {
4693 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
4694 } else {
4695 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
4696 };
4697 let group = diagnostics.find_map(|entry| {
4698 if entry.diagnostic.is_primary
4699 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
4700 && !entry.range.is_empty()
4701 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
4702 {
4703 Some((entry.range, entry.diagnostic.group_id))
4704 } else {
4705 None
4706 }
4707 });
4708
4709 if let Some((primary_range, group_id)) = group {
4710 if self.activate_diagnostics(group_id, cx) {
4711 self.change_selections(Some(Autoscroll::Center), cx, |s| {
4712 s.select(vec![Selection {
4713 id: selection.id,
4714 start: primary_range.start,
4715 end: primary_range.start,
4716 reversed: false,
4717 goal: SelectionGoal::None,
4718 }]);
4719 });
4720 }
4721 break;
4722 } else {
4723 // Cycle around to the start of the buffer, potentially moving back to the start of
4724 // the currently active diagnostic.
4725 active_primary_range.take();
4726 if direction == Direction::Prev {
4727 if search_start == buffer.len() {
4728 break;
4729 } else {
4730 search_start = buffer.len();
4731 }
4732 } else {
4733 if search_start == 0 {
4734 break;
4735 } else {
4736 search_start = 0;
4737 }
4738 }
4739 }
4740 }
4741 }
4742
4743 pub fn go_to_definition(
4744 workspace: &mut Workspace,
4745 _: &GoToDefinition,
4746 cx: &mut ViewContext<Workspace>,
4747 ) {
4748 Self::go_to_definition_of_kind(GotoDefinitionKind::Symbol, workspace, cx);
4749 }
4750
4751 pub fn go_to_type_definition(
4752 workspace: &mut Workspace,
4753 _: &GoToTypeDefinition,
4754 cx: &mut ViewContext<Workspace>,
4755 ) {
4756 Self::go_to_definition_of_kind(GotoDefinitionKind::Type, workspace, cx);
4757 }
4758
4759 fn go_to_definition_of_kind(
4760 kind: GotoDefinitionKind,
4761 workspace: &mut Workspace,
4762 cx: &mut ViewContext<Workspace>,
4763 ) {
4764 let active_item = workspace.active_item(cx);
4765 let editor_handle = if let Some(editor) = active_item
4766 .as_ref()
4767 .and_then(|item| item.act_as::<Self>(cx))
4768 {
4769 editor
4770 } else {
4771 return;
4772 };
4773
4774 let editor = editor_handle.read(cx);
4775 let buffer = editor.buffer.read(cx);
4776 let head = editor.selections.newest::<usize>(cx).head();
4777 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
4778 text_anchor
4779 } else {
4780 return;
4781 };
4782
4783 let project = workspace.project().clone();
4784 let definitions = project.update(cx, |project, cx| match kind {
4785 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
4786 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
4787 });
4788
4789 cx.spawn(|workspace, mut cx| async move {
4790 let definitions = definitions.await?;
4791 workspace.update(&mut cx, |workspace, cx| {
4792 Editor::navigate_to_definitions(workspace, editor_handle, definitions, cx);
4793 });
4794
4795 Ok::<(), anyhow::Error>(())
4796 })
4797 .detach_and_log_err(cx);
4798 }
4799
4800 pub fn navigate_to_definitions(
4801 workspace: &mut Workspace,
4802 editor_handle: ViewHandle<Editor>,
4803 definitions: Vec<LocationLink>,
4804 cx: &mut ViewContext<Workspace>,
4805 ) {
4806 let pane = workspace.active_pane().clone();
4807 for definition in definitions {
4808 let range = definition
4809 .target
4810 .range
4811 .to_offset(definition.target.buffer.read(cx));
4812
4813 let target_editor_handle = workspace.open_project_item(definition.target.buffer, cx);
4814 target_editor_handle.update(cx, |target_editor, cx| {
4815 // When selecting a definition in a different buffer, disable the nav history
4816 // to avoid creating a history entry at the previous cursor location.
4817 if editor_handle != target_editor_handle {
4818 pane.update(cx, |pane, _| pane.disable_history());
4819 }
4820 target_editor.change_selections(Some(Autoscroll::Center), cx, |s| {
4821 s.select_ranges([range]);
4822 });
4823
4824 pane.update(cx, |pane, _| pane.enable_history());
4825 });
4826 }
4827 }
4828
4829 pub fn find_all_references(
4830 workspace: &mut Workspace,
4831 _: &FindAllReferences,
4832 cx: &mut ViewContext<Workspace>,
4833 ) -> Option<Task<Result<()>>> {
4834 let active_item = workspace.active_item(cx)?;
4835 let editor_handle = active_item.act_as::<Self>(cx)?;
4836
4837 let editor = editor_handle.read(cx);
4838 let buffer = editor.buffer.read(cx);
4839 let head = editor.selections.newest::<usize>(cx).head();
4840 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
4841 let replica_id = editor.replica_id(cx);
4842
4843 let project = workspace.project().clone();
4844 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
4845 Some(cx.spawn(|workspace, mut cx| async move {
4846 let mut locations = references.await?;
4847 if locations.is_empty() {
4848 return Ok(());
4849 }
4850
4851 locations.sort_by_key(|location| location.buffer.id());
4852 let mut locations = locations.into_iter().peekable();
4853 let mut ranges_to_highlight = Vec::new();
4854
4855 let excerpt_buffer = cx.add_model(|cx| {
4856 let mut symbol_name = None;
4857 let mut multibuffer = MultiBuffer::new(replica_id);
4858 while let Some(location) = locations.next() {
4859 let buffer = location.buffer.read(cx);
4860 let mut ranges_for_buffer = Vec::new();
4861 let range = location.range.to_offset(buffer);
4862 ranges_for_buffer.push(range.clone());
4863 if symbol_name.is_none() {
4864 symbol_name = Some(buffer.text_for_range(range).collect::<String>());
4865 }
4866
4867 while let Some(next_location) = locations.peek() {
4868 if next_location.buffer == location.buffer {
4869 ranges_for_buffer.push(next_location.range.to_offset(buffer));
4870 locations.next();
4871 } else {
4872 break;
4873 }
4874 }
4875
4876 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
4877 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
4878 location.buffer.clone(),
4879 ranges_for_buffer,
4880 1,
4881 cx,
4882 ));
4883 }
4884 multibuffer.with_title(format!("References to `{}`", symbol_name.unwrap()))
4885 });
4886
4887 workspace.update(&mut cx, |workspace, cx| {
4888 let editor =
4889 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
4890 editor.update(cx, |editor, cx| {
4891 editor.highlight_background::<Self>(
4892 ranges_to_highlight,
4893 |theme| theme.editor.highlighted_line_background,
4894 cx,
4895 );
4896 });
4897 workspace.add_item(Box::new(editor), cx);
4898 });
4899
4900 Ok(())
4901 }))
4902 }
4903
4904 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
4905 use language::ToOffset as _;
4906
4907 let project = self.project.clone()?;
4908 let selection = self.selections.newest_anchor().clone();
4909 let (cursor_buffer, cursor_buffer_position) = self
4910 .buffer
4911 .read(cx)
4912 .text_anchor_for_position(selection.head(), cx)?;
4913 let (tail_buffer, _) = self
4914 .buffer
4915 .read(cx)
4916 .text_anchor_for_position(selection.tail(), cx)?;
4917 if tail_buffer != cursor_buffer {
4918 return None;
4919 }
4920
4921 let snapshot = cursor_buffer.read(cx).snapshot();
4922 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
4923 let prepare_rename = project.update(cx, |project, cx| {
4924 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
4925 });
4926
4927 Some(cx.spawn(|this, mut cx| async move {
4928 let rename_range = if let Some(range) = prepare_rename.await? {
4929 Some(range)
4930 } else {
4931 this.read_with(&cx, |this, cx| {
4932 let buffer = this.buffer.read(cx).snapshot(cx);
4933 let mut buffer_highlights = this
4934 .document_highlights_for_position(selection.head(), &buffer)
4935 .filter(|highlight| {
4936 highlight.start.excerpt_id() == selection.head().excerpt_id()
4937 && highlight.end.excerpt_id() == selection.head().excerpt_id()
4938 });
4939 buffer_highlights
4940 .next()
4941 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
4942 })
4943 };
4944 if let Some(rename_range) = rename_range {
4945 let rename_buffer_range = rename_range.to_offset(&snapshot);
4946 let cursor_offset_in_rename_range =
4947 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
4948
4949 this.update(&mut cx, |this, cx| {
4950 this.take_rename(false, cx);
4951 let style = this.style(cx);
4952 let buffer = this.buffer.read(cx).read(cx);
4953 let cursor_offset = selection.head().to_offset(&buffer);
4954 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
4955 let rename_end = rename_start + rename_buffer_range.len();
4956 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
4957 let mut old_highlight_id = None;
4958 let old_name: Arc<str> = buffer
4959 .chunks(rename_start..rename_end, true)
4960 .map(|chunk| {
4961 if old_highlight_id.is_none() {
4962 old_highlight_id = chunk.syntax_highlight_id;
4963 }
4964 chunk.text
4965 })
4966 .collect::<String>()
4967 .into();
4968
4969 drop(buffer);
4970
4971 // Position the selection in the rename editor so that it matches the current selection.
4972 this.show_local_selections = false;
4973 let rename_editor = cx.add_view(|cx| {
4974 let mut editor = Editor::single_line(None, cx);
4975 if let Some(old_highlight_id) = old_highlight_id {
4976 editor.override_text_style =
4977 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
4978 }
4979 editor.buffer.update(cx, |buffer, cx| {
4980 buffer.edit([(0..0, old_name.clone())], None, cx)
4981 });
4982 editor.select_all(&SelectAll, cx);
4983 editor
4984 });
4985
4986 let ranges = this
4987 .clear_background_highlights::<DocumentHighlightWrite>(cx)
4988 .into_iter()
4989 .flat_map(|(_, ranges)| ranges)
4990 .chain(
4991 this.clear_background_highlights::<DocumentHighlightRead>(cx)
4992 .into_iter()
4993 .flat_map(|(_, ranges)| ranges),
4994 )
4995 .collect();
4996
4997 this.highlight_text::<Rename>(
4998 ranges,
4999 HighlightStyle {
5000 fade_out: Some(style.rename_fade),
5001 ..Default::default()
5002 },
5003 cx,
5004 );
5005 cx.focus(&rename_editor);
5006 let block_id = this.insert_blocks(
5007 [BlockProperties {
5008 style: BlockStyle::Flex,
5009 position: range.start.clone(),
5010 height: 1,
5011 render: Arc::new({
5012 let editor = rename_editor.clone();
5013 move |cx: &mut BlockContext| {
5014 ChildView::new(editor.clone())
5015 .contained()
5016 .with_padding_left(cx.anchor_x)
5017 .boxed()
5018 }
5019 }),
5020 disposition: BlockDisposition::Below,
5021 }],
5022 cx,
5023 )[0];
5024 this.pending_rename = Some(RenameState {
5025 range,
5026 old_name,
5027 editor: rename_editor,
5028 block_id,
5029 });
5030 });
5031 }
5032
5033 Ok(())
5034 }))
5035 }
5036
5037 pub fn confirm_rename(
5038 workspace: &mut Workspace,
5039 _: &ConfirmRename,
5040 cx: &mut ViewContext<Workspace>,
5041 ) -> Option<Task<Result<()>>> {
5042 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
5043
5044 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
5045 let rename = editor.take_rename(false, cx)?;
5046 let buffer = editor.buffer.read(cx);
5047 let (start_buffer, start) =
5048 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
5049 let (end_buffer, end) =
5050 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
5051 if start_buffer == end_buffer {
5052 let new_name = rename.editor.read(cx).text(cx);
5053 Some((start_buffer, start..end, rename.old_name, new_name))
5054 } else {
5055 None
5056 }
5057 })?;
5058
5059 let rename = workspace.project().clone().update(cx, |project, cx| {
5060 project.perform_rename(
5061 buffer.clone(),
5062 range.start.clone(),
5063 new_name.clone(),
5064 true,
5065 cx,
5066 )
5067 });
5068
5069 Some(cx.spawn(|workspace, mut cx| async move {
5070 let project_transaction = rename.await?;
5071 Self::open_project_transaction(
5072 editor.clone(),
5073 workspace,
5074 project_transaction,
5075 format!("Rename: {} → {}", old_name, new_name),
5076 cx.clone(),
5077 )
5078 .await?;
5079
5080 editor.update(&mut cx, |editor, cx| {
5081 editor.refresh_document_highlights(cx);
5082 });
5083 Ok(())
5084 }))
5085 }
5086
5087 fn take_rename(
5088 &mut self,
5089 moving_cursor: bool,
5090 cx: &mut ViewContext<Self>,
5091 ) -> Option<RenameState> {
5092 let rename = self.pending_rename.take()?;
5093 self.remove_blocks([rename.block_id].into_iter().collect(), cx);
5094 self.clear_text_highlights::<Rename>(cx);
5095 self.show_local_selections = true;
5096
5097 if moving_cursor {
5098 let rename_editor = rename.editor.read(cx);
5099 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
5100
5101 // Update the selection to match the position of the selection inside
5102 // the rename editor.
5103 let snapshot = self.buffer.read(cx).read(cx);
5104 let rename_range = rename.range.to_offset(&snapshot);
5105 let cursor_in_editor = snapshot
5106 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
5107 .min(rename_range.end);
5108 drop(snapshot);
5109
5110 self.change_selections(None, cx, |s| {
5111 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
5112 });
5113 } else {
5114 self.refresh_document_highlights(cx);
5115 }
5116
5117 Some(rename)
5118 }
5119
5120 #[cfg(any(test, feature = "test-support"))]
5121 pub fn pending_rename(&self) -> Option<&RenameState> {
5122 self.pending_rename.as_ref()
5123 }
5124
5125 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
5126 if let Some(project) = self.project.clone() {
5127 self.buffer.update(cx, |multi_buffer, cx| {
5128 project.update(cx, |project, cx| {
5129 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
5130 });
5131 })
5132 }
5133 }
5134
5135 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
5136 cx.show_character_palette();
5137 }
5138
5139 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
5140 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
5141 let buffer = self.buffer.read(cx).snapshot(cx);
5142 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
5143 let is_valid = buffer
5144 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
5145 .any(|entry| {
5146 entry.diagnostic.is_primary
5147 && !entry.range.is_empty()
5148 && entry.range.start == primary_range_start
5149 && entry.diagnostic.message == active_diagnostics.primary_message
5150 });
5151
5152 if is_valid != active_diagnostics.is_valid {
5153 active_diagnostics.is_valid = is_valid;
5154 let mut new_styles = HashMap::default();
5155 for (block_id, diagnostic) in &active_diagnostics.blocks {
5156 new_styles.insert(
5157 *block_id,
5158 diagnostic_block_renderer(diagnostic.clone(), is_valid),
5159 );
5160 }
5161 self.display_map
5162 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
5163 }
5164 }
5165 }
5166
5167 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
5168 self.dismiss_diagnostics(cx);
5169 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
5170 let buffer = self.buffer.read(cx).snapshot(cx);
5171
5172 let mut primary_range = None;
5173 let mut primary_message = None;
5174 let mut group_end = Point::zero();
5175 let diagnostic_group = buffer
5176 .diagnostic_group::<Point>(group_id)
5177 .map(|entry| {
5178 if entry.range.end > group_end {
5179 group_end = entry.range.end;
5180 }
5181 if entry.diagnostic.is_primary {
5182 primary_range = Some(entry.range.clone());
5183 primary_message = Some(entry.diagnostic.message.clone());
5184 }
5185 entry
5186 })
5187 .collect::<Vec<_>>();
5188 let primary_range = primary_range?;
5189 let primary_message = primary_message?;
5190 let primary_range =
5191 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
5192
5193 let blocks = display_map
5194 .insert_blocks(
5195 diagnostic_group.iter().map(|entry| {
5196 let diagnostic = entry.diagnostic.clone();
5197 let message_height = diagnostic.message.lines().count() as u8;
5198 BlockProperties {
5199 style: BlockStyle::Fixed,
5200 position: buffer.anchor_after(entry.range.start),
5201 height: message_height,
5202 render: diagnostic_block_renderer(diagnostic, true),
5203 disposition: BlockDisposition::Below,
5204 }
5205 }),
5206 cx,
5207 )
5208 .into_iter()
5209 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
5210 .collect();
5211
5212 Some(ActiveDiagnosticGroup {
5213 primary_range,
5214 primary_message,
5215 blocks,
5216 is_valid: true,
5217 })
5218 });
5219 self.active_diagnostics.is_some()
5220 }
5221
5222 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
5223 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
5224 self.display_map.update(cx, |display_map, cx| {
5225 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
5226 });
5227 cx.notify();
5228 }
5229 }
5230
5231 pub fn set_selections_from_remote(
5232 &mut self,
5233 selections: Vec<Selection<Anchor>>,
5234 cx: &mut ViewContext<Self>,
5235 ) {
5236 let old_cursor_position = self.selections.newest_anchor().head();
5237 self.selections.change_with(cx, |s| {
5238 s.select_anchors(selections);
5239 });
5240 self.selections_did_change(false, &old_cursor_position, cx);
5241 }
5242
5243 fn push_to_selection_history(&mut self) {
5244 self.selection_history.push(SelectionHistoryEntry {
5245 selections: self.selections.disjoint_anchors().clone(),
5246 select_next_state: self.select_next_state.clone(),
5247 add_selections_state: self.add_selections_state.clone(),
5248 });
5249 }
5250
5251 pub fn request_autoscroll(&mut self, autoscroll: Autoscroll, cx: &mut ViewContext<Self>) {
5252 self.autoscroll_request = Some((autoscroll, true));
5253 cx.notify();
5254 }
5255
5256 fn request_autoscroll_remotely(&mut self, autoscroll: Autoscroll, cx: &mut ViewContext<Self>) {
5257 self.autoscroll_request = Some((autoscroll, false));
5258 cx.notify();
5259 }
5260
5261 pub fn transact(
5262 &mut self,
5263 cx: &mut ViewContext<Self>,
5264 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
5265 ) -> Option<TransactionId> {
5266 self.start_transaction_at(Instant::now(), cx);
5267 update(self, cx);
5268 self.end_transaction_at(Instant::now(), cx)
5269 }
5270
5271 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
5272 self.end_selection(cx);
5273 if let Some(tx_id) = self
5274 .buffer
5275 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
5276 {
5277 self.selection_history
5278 .insert_transaction(tx_id, self.selections.disjoint_anchors().clone());
5279 }
5280 }
5281
5282 fn end_transaction_at(
5283 &mut self,
5284 now: Instant,
5285 cx: &mut ViewContext<Self>,
5286 ) -> Option<TransactionId> {
5287 if let Some(tx_id) = self
5288 .buffer
5289 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
5290 {
5291 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
5292 *end_selections = Some(self.selections.disjoint_anchors().clone());
5293 } else {
5294 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
5295 }
5296
5297 cx.emit(Event::Edited);
5298 Some(tx_id)
5299 } else {
5300 None
5301 }
5302 }
5303
5304 pub fn page_up(&mut self, _: &PageUp, _: &mut ViewContext<Self>) {
5305 log::info!("Editor::page_up");
5306 }
5307
5308 pub fn page_down(&mut self, _: &PageDown, _: &mut ViewContext<Self>) {
5309 log::info!("Editor::page_down");
5310 }
5311
5312 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
5313 let mut fold_ranges = Vec::new();
5314
5315 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5316 let selections = self.selections.all::<Point>(cx);
5317 for selection in selections {
5318 let range = selection.display_range(&display_map).sorted();
5319 let buffer_start_row = range.start.to_point(&display_map).row;
5320
5321 for row in (0..=range.end.row()).rev() {
5322 if self.is_line_foldable(&display_map, row) && !display_map.is_line_folded(row) {
5323 let fold_range = self.foldable_range_for_line(&display_map, row);
5324 if fold_range.end.row >= buffer_start_row {
5325 fold_ranges.push(fold_range);
5326 if row <= range.start.row() {
5327 break;
5328 }
5329 }
5330 }
5331 }
5332 }
5333
5334 self.fold_ranges(fold_ranges, cx);
5335 }
5336
5337 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
5338 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5339 let buffer = &display_map.buffer_snapshot;
5340 let selections = self.selections.all::<Point>(cx);
5341 let ranges = selections
5342 .iter()
5343 .map(|s| {
5344 let range = s.display_range(&display_map).sorted();
5345 let mut start = range.start.to_point(&display_map);
5346 let mut end = range.end.to_point(&display_map);
5347 start.column = 0;
5348 end.column = buffer.line_len(end.row);
5349 start..end
5350 })
5351 .collect::<Vec<_>>();
5352 self.unfold_ranges(ranges, true, cx);
5353 }
5354
5355 fn is_line_foldable(&self, display_map: &DisplaySnapshot, display_row: u32) -> bool {
5356 let max_point = display_map.max_point();
5357 if display_row >= max_point.row() {
5358 false
5359 } else {
5360 let (start_indent, is_blank) = display_map.line_indent(display_row);
5361 if is_blank {
5362 false
5363 } else {
5364 for display_row in display_row + 1..=max_point.row() {
5365 let (indent, is_blank) = display_map.line_indent(display_row);
5366 if !is_blank {
5367 return indent > start_indent;
5368 }
5369 }
5370 false
5371 }
5372 }
5373 }
5374
5375 fn foldable_range_for_line(
5376 &self,
5377 display_map: &DisplaySnapshot,
5378 start_row: u32,
5379 ) -> Range<Point> {
5380 let max_point = display_map.max_point();
5381
5382 let (start_indent, _) = display_map.line_indent(start_row);
5383 let start = DisplayPoint::new(start_row, display_map.line_len(start_row));
5384 let mut end = None;
5385 for row in start_row + 1..=max_point.row() {
5386 let (indent, is_blank) = display_map.line_indent(row);
5387 if !is_blank && indent <= start_indent {
5388 end = Some(DisplayPoint::new(row - 1, display_map.line_len(row - 1)));
5389 break;
5390 }
5391 }
5392
5393 let end = end.unwrap_or(max_point);
5394 return start.to_point(display_map)..end.to_point(display_map);
5395 }
5396
5397 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
5398 let selections = self.selections.all::<Point>(cx);
5399 let ranges = selections.into_iter().map(|s| s.start..s.end);
5400 self.fold_ranges(ranges, cx);
5401 }
5402
5403 pub fn fold_ranges<T: ToOffset>(
5404 &mut self,
5405 ranges: impl IntoIterator<Item = Range<T>>,
5406 cx: &mut ViewContext<Self>,
5407 ) {
5408 let mut ranges = ranges.into_iter().peekable();
5409 if ranges.peek().is_some() {
5410 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
5411 self.request_autoscroll(Autoscroll::Fit, cx);
5412 cx.notify();
5413 }
5414 }
5415
5416 pub fn unfold_ranges<T: ToOffset>(
5417 &mut self,
5418 ranges: impl IntoIterator<Item = Range<T>>,
5419 inclusive: bool,
5420 cx: &mut ViewContext<Self>,
5421 ) {
5422 let mut ranges = ranges.into_iter().peekable();
5423 if ranges.peek().is_some() {
5424 self.display_map
5425 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
5426 self.request_autoscroll(Autoscroll::Fit, cx);
5427 cx.notify();
5428 }
5429 }
5430
5431 pub fn insert_blocks(
5432 &mut self,
5433 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
5434 cx: &mut ViewContext<Self>,
5435 ) -> Vec<BlockId> {
5436 let blocks = self
5437 .display_map
5438 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
5439 self.request_autoscroll(Autoscroll::Fit, cx);
5440 blocks
5441 }
5442
5443 pub fn replace_blocks(
5444 &mut self,
5445 blocks: HashMap<BlockId, RenderBlock>,
5446 cx: &mut ViewContext<Self>,
5447 ) {
5448 self.display_map
5449 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
5450 self.request_autoscroll(Autoscroll::Fit, cx);
5451 }
5452
5453 pub fn remove_blocks(&mut self, block_ids: HashSet<BlockId>, cx: &mut ViewContext<Self>) {
5454 self.display_map.update(cx, |display_map, cx| {
5455 display_map.remove_blocks(block_ids, cx)
5456 });
5457 }
5458
5459 pub fn longest_row(&self, cx: &mut MutableAppContext) -> u32 {
5460 self.display_map
5461 .update(cx, |map, cx| map.snapshot(cx))
5462 .longest_row()
5463 }
5464
5465 pub fn max_point(&self, cx: &mut MutableAppContext) -> DisplayPoint {
5466 self.display_map
5467 .update(cx, |map, cx| map.snapshot(cx))
5468 .max_point()
5469 }
5470
5471 pub fn text(&self, cx: &AppContext) -> String {
5472 self.buffer.read(cx).read(cx).text()
5473 }
5474
5475 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
5476 self.transact(cx, |this, cx| {
5477 this.buffer
5478 .read(cx)
5479 .as_singleton()
5480 .expect("you can only call set_text on editors for singleton buffers")
5481 .update(cx, |buffer, cx| buffer.set_text(text, cx));
5482 });
5483 }
5484
5485 pub fn display_text(&self, cx: &mut MutableAppContext) -> String {
5486 self.display_map
5487 .update(cx, |map, cx| map.snapshot(cx))
5488 .text()
5489 }
5490
5491 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
5492 let language_name = self
5493 .buffer
5494 .read(cx)
5495 .as_singleton()
5496 .and_then(|singleton_buffer| singleton_buffer.read(cx).language())
5497 .map(|l| l.name());
5498
5499 let settings = cx.global::<Settings>();
5500 let mode = self
5501 .soft_wrap_mode_override
5502 .unwrap_or_else(|| settings.soft_wrap(language_name.as_deref()));
5503 match mode {
5504 settings::SoftWrap::None => SoftWrap::None,
5505 settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
5506 settings::SoftWrap::PreferredLineLength => {
5507 SoftWrap::Column(settings.preferred_line_length(language_name.as_deref()))
5508 }
5509 }
5510 }
5511
5512 pub fn set_soft_wrap_mode(&mut self, mode: settings::SoftWrap, cx: &mut ViewContext<Self>) {
5513 self.soft_wrap_mode_override = Some(mode);
5514 cx.notify();
5515 }
5516
5517 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut MutableAppContext) -> bool {
5518 self.display_map
5519 .update(cx, |map, cx| map.set_wrap_width(width, cx))
5520 }
5521
5522 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
5523 self.highlighted_rows = rows;
5524 }
5525
5526 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
5527 self.highlighted_rows.clone()
5528 }
5529
5530 pub fn highlight_background<T: 'static>(
5531 &mut self,
5532 ranges: Vec<Range<Anchor>>,
5533 color_fetcher: fn(&Theme) -> Color,
5534 cx: &mut ViewContext<Self>,
5535 ) {
5536 self.background_highlights
5537 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
5538 cx.notify();
5539 }
5540
5541 pub fn clear_background_highlights<T: 'static>(
5542 &mut self,
5543 cx: &mut ViewContext<Self>,
5544 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
5545 cx.notify();
5546 self.background_highlights.remove(&TypeId::of::<T>())
5547 }
5548
5549 #[cfg(feature = "test-support")]
5550 pub fn all_background_highlights(
5551 &mut self,
5552 cx: &mut ViewContext<Self>,
5553 ) -> Vec<(Range<DisplayPoint>, Color)> {
5554 let snapshot = self.snapshot(cx);
5555 let buffer = &snapshot.buffer_snapshot;
5556 let start = buffer.anchor_before(0);
5557 let end = buffer.anchor_after(buffer.len());
5558 let theme = cx.global::<Settings>().theme.as_ref();
5559 self.background_highlights_in_range(start..end, &snapshot, theme)
5560 }
5561
5562 fn document_highlights_for_position<'a>(
5563 &'a self,
5564 position: Anchor,
5565 buffer: &'a MultiBufferSnapshot,
5566 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
5567 let read_highlights = self
5568 .background_highlights
5569 .get(&TypeId::of::<DocumentHighlightRead>())
5570 .map(|h| &h.1);
5571 let write_highlights = self
5572 .background_highlights
5573 .get(&TypeId::of::<DocumentHighlightWrite>())
5574 .map(|h| &h.1);
5575 let left_position = position.bias_left(buffer);
5576 let right_position = position.bias_right(buffer);
5577 read_highlights
5578 .into_iter()
5579 .chain(write_highlights)
5580 .flat_map(move |ranges| {
5581 let start_ix = match ranges.binary_search_by(|probe| {
5582 let cmp = probe.end.cmp(&left_position, &buffer);
5583 if cmp.is_ge() {
5584 Ordering::Greater
5585 } else {
5586 Ordering::Less
5587 }
5588 }) {
5589 Ok(i) | Err(i) => i,
5590 };
5591
5592 let right_position = right_position.clone();
5593 ranges[start_ix..]
5594 .iter()
5595 .take_while(move |range| range.start.cmp(&right_position, &buffer).is_le())
5596 })
5597 }
5598
5599 pub fn background_highlights_in_range(
5600 &self,
5601 search_range: Range<Anchor>,
5602 display_snapshot: &DisplaySnapshot,
5603 theme: &Theme,
5604 ) -> Vec<(Range<DisplayPoint>, Color)> {
5605 let mut results = Vec::new();
5606 let buffer = &display_snapshot.buffer_snapshot;
5607 for (color_fetcher, ranges) in self.background_highlights.values() {
5608 let color = color_fetcher(theme);
5609 let start_ix = match ranges.binary_search_by(|probe| {
5610 let cmp = probe.end.cmp(&search_range.start, &buffer);
5611 if cmp.is_gt() {
5612 Ordering::Greater
5613 } else {
5614 Ordering::Less
5615 }
5616 }) {
5617 Ok(i) | Err(i) => i,
5618 };
5619 for range in &ranges[start_ix..] {
5620 if range.start.cmp(&search_range.end, &buffer).is_ge() {
5621 break;
5622 }
5623 let start = range
5624 .start
5625 .to_point(buffer)
5626 .to_display_point(display_snapshot);
5627 let end = range
5628 .end
5629 .to_point(buffer)
5630 .to_display_point(display_snapshot);
5631 results.push((start..end, color))
5632 }
5633 }
5634 results
5635 }
5636
5637 pub fn highlight_text<T: 'static>(
5638 &mut self,
5639 ranges: Vec<Range<Anchor>>,
5640 style: HighlightStyle,
5641 cx: &mut ViewContext<Self>,
5642 ) {
5643 self.display_map.update(cx, |map, _| {
5644 map.highlight_text(TypeId::of::<T>(), ranges, style)
5645 });
5646 cx.notify();
5647 }
5648
5649 pub fn text_highlights<'a, T: 'static>(
5650 &'a self,
5651 cx: &'a AppContext,
5652 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
5653 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
5654 }
5655
5656 pub fn clear_text_highlights<T: 'static>(
5657 &mut self,
5658 cx: &mut ViewContext<Self>,
5659 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
5660 cx.notify();
5661 self.display_map
5662 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()))
5663 }
5664
5665 fn next_blink_epoch(&mut self) -> usize {
5666 self.blink_epoch += 1;
5667 self.blink_epoch
5668 }
5669
5670 fn pause_cursor_blinking(&mut self, cx: &mut ViewContext<Self>) {
5671 if !self.focused {
5672 return;
5673 }
5674
5675 self.show_local_cursors = true;
5676 cx.notify();
5677
5678 let epoch = self.next_blink_epoch();
5679 cx.spawn(|this, mut cx| {
5680 let this = this.downgrade();
5681 async move {
5682 Timer::after(CURSOR_BLINK_INTERVAL).await;
5683 if let Some(this) = this.upgrade(&cx) {
5684 this.update(&mut cx, |this, cx| this.resume_cursor_blinking(epoch, cx))
5685 }
5686 }
5687 })
5688 .detach();
5689 }
5690
5691 fn resume_cursor_blinking(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
5692 if epoch == self.blink_epoch {
5693 self.blinking_paused = false;
5694 self.blink_cursors(epoch, cx);
5695 }
5696 }
5697
5698 fn blink_cursors(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
5699 if epoch == self.blink_epoch && self.focused && !self.blinking_paused {
5700 self.show_local_cursors = !self.show_local_cursors;
5701 cx.notify();
5702
5703 let epoch = self.next_blink_epoch();
5704 cx.spawn(|this, mut cx| {
5705 let this = this.downgrade();
5706 async move {
5707 Timer::after(CURSOR_BLINK_INTERVAL).await;
5708 if let Some(this) = this.upgrade(&cx) {
5709 this.update(&mut cx, |this, cx| this.blink_cursors(epoch, cx));
5710 }
5711 }
5712 })
5713 .detach();
5714 }
5715 }
5716
5717 pub fn show_local_cursors(&self) -> bool {
5718 self.show_local_cursors && self.focused
5719 }
5720
5721 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
5722 cx.notify();
5723 }
5724
5725 fn on_buffer_event(
5726 &mut self,
5727 _: ModelHandle<MultiBuffer>,
5728 event: &language::Event,
5729 cx: &mut ViewContext<Self>,
5730 ) {
5731 match event {
5732 language::Event::Edited => {
5733 self.refresh_active_diagnostics(cx);
5734 self.refresh_code_actions(cx);
5735 cx.emit(Event::BufferEdited);
5736 }
5737 language::Event::Reparsed => cx.emit(Event::Reparsed),
5738 language::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
5739 language::Event::Saved => cx.emit(Event::Saved),
5740 language::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
5741 language::Event::Reloaded => cx.emit(Event::TitleChanged),
5742 language::Event::Closed => cx.emit(Event::Closed),
5743 language::Event::DiagnosticsUpdated => {
5744 self.refresh_active_diagnostics(cx);
5745 }
5746 _ => {}
5747 }
5748 }
5749
5750 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
5751 cx.notify();
5752 }
5753
5754 pub fn set_searchable(&mut self, searchable: bool) {
5755 self.searchable = searchable;
5756 }
5757
5758 pub fn searchable(&self) -> bool {
5759 self.searchable
5760 }
5761
5762 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
5763 let active_item = workspace.active_item(cx);
5764 let editor_handle = if let Some(editor) = active_item
5765 .as_ref()
5766 .and_then(|item| item.act_as::<Self>(cx))
5767 {
5768 editor
5769 } else {
5770 cx.propagate_action();
5771 return;
5772 };
5773
5774 let editor = editor_handle.read(cx);
5775 let buffer = editor.buffer.read(cx);
5776 if buffer.is_singleton() {
5777 cx.propagate_action();
5778 return;
5779 }
5780
5781 let mut new_selections_by_buffer = HashMap::default();
5782 for selection in editor.selections.all::<usize>(cx) {
5783 for (buffer, mut range) in
5784 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
5785 {
5786 if selection.reversed {
5787 mem::swap(&mut range.start, &mut range.end);
5788 }
5789 new_selections_by_buffer
5790 .entry(buffer)
5791 .or_insert(Vec::new())
5792 .push(range)
5793 }
5794 }
5795
5796 editor_handle.update(cx, |editor, cx| {
5797 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
5798 });
5799 let pane = workspace.active_pane().clone();
5800 pane.update(cx, |pane, _| pane.disable_history());
5801
5802 // We defer the pane interaction because we ourselves are a workspace item
5803 // and activating a new item causes the pane to call a method on us reentrantly,
5804 // which panics if we're on the stack.
5805 cx.defer(move |workspace, cx| {
5806 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
5807 let editor = workspace.open_project_item::<Self>(buffer, cx);
5808 editor.update(cx, |editor, cx| {
5809 editor.change_selections(Some(Autoscroll::Newest), cx, |s| {
5810 s.select_ranges(ranges);
5811 });
5812 });
5813 }
5814
5815 pane.update(cx, |pane, _| pane.enable_history());
5816 });
5817 }
5818
5819 fn jump(workspace: &mut Workspace, action: &Jump, cx: &mut ViewContext<Workspace>) {
5820 let editor = workspace.open_path(action.path.clone(), true, cx);
5821 let position = action.position;
5822 let anchor = action.anchor;
5823 cx.spawn_weak(|_, mut cx| async move {
5824 let editor = editor.await.log_err()?.downcast::<Editor>()?;
5825 editor.update(&mut cx, |editor, cx| {
5826 let buffer = editor.buffer().read(cx).as_singleton()?;
5827 let buffer = buffer.read(cx);
5828 let cursor = if buffer.can_resolve(&anchor) {
5829 language::ToPoint::to_point(&anchor, buffer)
5830 } else {
5831 buffer.clip_point(position, Bias::Left)
5832 };
5833
5834 let nav_history = editor.nav_history.take();
5835 editor.change_selections(Some(Autoscroll::Newest), cx, |s| {
5836 s.select_ranges([cursor..cursor]);
5837 });
5838 editor.nav_history = nav_history;
5839
5840 Some(())
5841 })?;
5842 Some(())
5843 })
5844 .detach()
5845 }
5846
5847 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
5848 let snapshot = self.buffer.read(cx).read(cx);
5849 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
5850 Some(
5851 ranges
5852 .into_iter()
5853 .map(move |range| {
5854 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
5855 })
5856 .collect(),
5857 )
5858 }
5859
5860 fn selection_replacement_ranges(
5861 &self,
5862 range: Range<OffsetUtf16>,
5863 cx: &AppContext,
5864 ) -> Vec<Range<OffsetUtf16>> {
5865 let selections = self.selections.all::<OffsetUtf16>(cx);
5866 let newest_selection = selections
5867 .iter()
5868 .max_by_key(|selection| selection.id)
5869 .unwrap();
5870 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
5871 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
5872 let snapshot = self.buffer.read(cx).read(cx);
5873 selections
5874 .into_iter()
5875 .map(|mut selection| {
5876 selection.start.0 =
5877 (selection.start.0 as isize).saturating_add(start_delta) as usize;
5878 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
5879 snapshot.clip_offset_utf16(selection.start, Bias::Left)
5880 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
5881 })
5882 .collect()
5883 }
5884}
5885
5886impl EditorSnapshot {
5887 pub fn is_focused(&self) -> bool {
5888 self.is_focused
5889 }
5890
5891 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
5892 self.placeholder_text.as_ref()
5893 }
5894
5895 pub fn scroll_position(&self) -> Vector2F {
5896 compute_scroll_position(
5897 &self.display_snapshot,
5898 self.scroll_position,
5899 &self.scroll_top_anchor,
5900 )
5901 }
5902}
5903
5904impl Deref for EditorSnapshot {
5905 type Target = DisplaySnapshot;
5906
5907 fn deref(&self) -> &Self::Target {
5908 &self.display_snapshot
5909 }
5910}
5911
5912fn compute_scroll_position(
5913 snapshot: &DisplaySnapshot,
5914 mut scroll_position: Vector2F,
5915 scroll_top_anchor: &Anchor,
5916) -> Vector2F {
5917 if *scroll_top_anchor != Anchor::min() {
5918 let scroll_top = scroll_top_anchor.to_display_point(snapshot).row() as f32;
5919 scroll_position.set_y(scroll_top + scroll_position.y());
5920 } else {
5921 scroll_position.set_y(0.);
5922 }
5923 scroll_position
5924}
5925
5926#[derive(Copy, Clone, Debug, PartialEq, Eq)]
5927pub enum Event {
5928 Activate,
5929 BufferEdited,
5930 Edited,
5931 Reparsed,
5932 Blurred,
5933 DirtyChanged,
5934 Saved,
5935 TitleChanged,
5936 SelectionsChanged { local: bool },
5937 ScrollPositionChanged { local: bool },
5938 Closed,
5939 IgnoredInput,
5940}
5941
5942pub struct EditorFocused(pub ViewHandle<Editor>);
5943pub struct EditorBlurred(pub ViewHandle<Editor>);
5944pub struct EditorReleased(pub WeakViewHandle<Editor>);
5945
5946impl Entity for Editor {
5947 type Event = Event;
5948
5949 fn release(&mut self, cx: &mut MutableAppContext) {
5950 cx.emit_global(EditorReleased(self.handle.clone()));
5951 }
5952}
5953
5954impl View for Editor {
5955 fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
5956 let style = self.style(cx);
5957 let font_changed = self.display_map.update(cx, |map, cx| {
5958 map.set_font(style.text.font_id, style.text.font_size, cx)
5959 });
5960
5961 if font_changed {
5962 let handle = self.handle.clone();
5963 cx.defer(move |cx| {
5964 if let Some(editor) = handle.upgrade(cx) {
5965 editor.update(cx, |editor, cx| {
5966 hide_hover(editor, cx);
5967 })
5968 }
5969 });
5970 }
5971
5972 Stack::new()
5973 .with_child(
5974 EditorElement::new(self.handle.clone(), style.clone(), self.cursor_shape).boxed(),
5975 )
5976 .with_child(ChildView::new(&self.mouse_context_menu).boxed())
5977 .boxed()
5978 }
5979
5980 fn ui_name() -> &'static str {
5981 "Editor"
5982 }
5983
5984 fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
5985 let focused_event = EditorFocused(cx.handle());
5986 cx.emit_global(focused_event);
5987 if let Some(rename) = self.pending_rename.as_ref() {
5988 cx.focus(&rename.editor);
5989 } else {
5990 self.focused = true;
5991 self.blink_cursors(self.blink_epoch, cx);
5992 self.buffer.update(cx, |buffer, cx| {
5993 buffer.finalize_last_transaction(cx);
5994 if self.leader_replica_id.is_none() {
5995 buffer.set_active_selections(
5996 &self.selections.disjoint_anchors(),
5997 self.selections.line_mode,
5998 cx,
5999 );
6000 }
6001 });
6002 }
6003 }
6004
6005 fn on_blur(&mut self, cx: &mut ViewContext<Self>) {
6006 let blurred_event = EditorBlurred(cx.handle());
6007 cx.emit_global(blurred_event);
6008 self.focused = false;
6009 self.buffer
6010 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
6011 self.hide_context_menu(cx);
6012 hide_hover(self, cx);
6013 cx.emit(Event::Blurred);
6014 cx.notify();
6015 }
6016
6017 fn keymap_context(&self, _: &AppContext) -> gpui::keymap::Context {
6018 let mut context = Self::default_keymap_context();
6019 let mode = match self.mode {
6020 EditorMode::SingleLine => "single_line",
6021 EditorMode::AutoHeight { .. } => "auto_height",
6022 EditorMode::Full => "full",
6023 };
6024 context.map.insert("mode".into(), mode.into());
6025 if self.pending_rename.is_some() {
6026 context.set.insert("renaming".into());
6027 }
6028 match self.context_menu.as_ref() {
6029 Some(ContextMenu::Completions(_)) => {
6030 context.set.insert("showing_completions".into());
6031 }
6032 Some(ContextMenu::CodeActions(_)) => {
6033 context.set.insert("showing_code_actions".into());
6034 }
6035 None => {}
6036 }
6037
6038 for layer in self.keymap_context_layers.values() {
6039 context.extend(layer);
6040 }
6041
6042 context
6043 }
6044
6045 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
6046 Some(
6047 self.buffer
6048 .read(cx)
6049 .read(cx)
6050 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
6051 .collect(),
6052 )
6053 }
6054
6055 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
6056 // Prevent the IME menu from appearing when holding down an alphabetic key
6057 // while input is disabled.
6058 if !self.input_enabled {
6059 return None;
6060 }
6061
6062 let range = self.selections.newest::<OffsetUtf16>(cx).range();
6063 Some(range.start.0..range.end.0)
6064 }
6065
6066 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
6067 let snapshot = self.buffer.read(cx).read(cx);
6068 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
6069 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
6070 }
6071
6072 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
6073 self.clear_text_highlights::<InputComposition>(cx);
6074 self.ime_transaction.take();
6075 }
6076
6077 fn replace_text_in_range(
6078 &mut self,
6079 range_utf16: Option<Range<usize>>,
6080 text: &str,
6081 cx: &mut ViewContext<Self>,
6082 ) {
6083 if !self.input_enabled {
6084 cx.emit(Event::IgnoredInput);
6085 return;
6086 }
6087
6088 self.transact(cx, |this, cx| {
6089 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
6090 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
6091 Some(this.selection_replacement_ranges(range_utf16, cx))
6092 } else if let Some(marked_ranges) = this.marked_text_ranges(cx) {
6093 Some(marked_ranges)
6094 } else {
6095 None
6096 };
6097
6098 if let Some(new_selected_ranges) = new_selected_ranges {
6099 this.change_selections(None, cx, |selections| {
6100 selections.select_ranges(new_selected_ranges)
6101 });
6102 }
6103 this.handle_input(text, cx);
6104 });
6105
6106 if let Some(transaction) = self.ime_transaction {
6107 self.buffer.update(cx, |buffer, cx| {
6108 buffer.group_until_transaction(transaction, cx);
6109 });
6110 }
6111
6112 self.unmark_text(cx);
6113 }
6114
6115 fn replace_and_mark_text_in_range(
6116 &mut self,
6117 range_utf16: Option<Range<usize>>,
6118 text: &str,
6119 new_selected_range_utf16: Option<Range<usize>>,
6120 cx: &mut ViewContext<Self>,
6121 ) {
6122 if !self.input_enabled {
6123 cx.emit(Event::IgnoredInput);
6124 return;
6125 }
6126
6127 let transaction = self.transact(cx, |this, cx| {
6128 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
6129 let snapshot = this.buffer.read(cx).read(cx);
6130 if let Some(relative_range_utf16) = range_utf16.as_ref() {
6131 for marked_range in &mut marked_ranges {
6132 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
6133 marked_range.start.0 += relative_range_utf16.start;
6134 marked_range.start =
6135 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
6136 marked_range.end =
6137 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
6138 }
6139 }
6140 Some(marked_ranges)
6141 } else if let Some(range_utf16) = range_utf16 {
6142 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
6143 Some(this.selection_replacement_ranges(range_utf16, cx))
6144 } else {
6145 None
6146 };
6147
6148 if let Some(ranges) = ranges_to_replace {
6149 this.change_selections(None, cx, |s| s.select_ranges(ranges));
6150 }
6151
6152 let marked_ranges = {
6153 let snapshot = this.buffer.read(cx).read(cx);
6154 this.selections
6155 .disjoint_anchors()
6156 .into_iter()
6157 .map(|selection| {
6158 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
6159 })
6160 .collect::<Vec<_>>()
6161 };
6162
6163 if text.is_empty() {
6164 this.unmark_text(cx);
6165 } else {
6166 this.highlight_text::<InputComposition>(
6167 marked_ranges.clone(),
6168 this.style(cx).composition_mark,
6169 cx,
6170 );
6171 }
6172
6173 this.handle_input(text, cx);
6174
6175 if let Some(new_selected_range) = new_selected_range_utf16 {
6176 let snapshot = this.buffer.read(cx).read(cx);
6177 let new_selected_ranges = marked_ranges
6178 .into_iter()
6179 .map(|marked_range| {
6180 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
6181 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
6182 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
6183 snapshot.clip_offset_utf16(new_start, Bias::Left)
6184 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
6185 })
6186 .collect::<Vec<_>>();
6187
6188 drop(snapshot);
6189 this.change_selections(None, cx, |selections| {
6190 selections.select_ranges(new_selected_ranges)
6191 });
6192 }
6193 });
6194
6195 self.ime_transaction = self.ime_transaction.or(transaction);
6196 if let Some(transaction) = self.ime_transaction {
6197 self.buffer.update(cx, |buffer, cx| {
6198 buffer.group_until_transaction(transaction, cx);
6199 });
6200 }
6201
6202 if self.text_highlights::<InputComposition>(cx).is_none() {
6203 self.ime_transaction.take();
6204 }
6205 }
6206}
6207
6208fn build_style(
6209 settings: &Settings,
6210 get_field_editor_theme: Option<GetFieldEditorTheme>,
6211 override_text_style: Option<&OverrideTextStyle>,
6212 cx: &AppContext,
6213) -> EditorStyle {
6214 let font_cache = cx.font_cache();
6215
6216 let mut theme = settings.theme.editor.clone();
6217 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
6218 let field_editor_theme = get_field_editor_theme(&settings.theme);
6219 theme.text_color = field_editor_theme.text.color;
6220 theme.selection = field_editor_theme.selection;
6221 theme.background = field_editor_theme
6222 .container
6223 .background_color
6224 .unwrap_or_default();
6225 EditorStyle {
6226 text: field_editor_theme.text,
6227 placeholder_text: field_editor_theme.placeholder_text,
6228 theme,
6229 }
6230 } else {
6231 let font_family_id = settings.buffer_font_family;
6232 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
6233 let font_properties = Default::default();
6234 let font_id = font_cache
6235 .select_font(font_family_id, &font_properties)
6236 .unwrap();
6237 let font_size = settings.buffer_font_size;
6238 EditorStyle {
6239 text: TextStyle {
6240 color: settings.theme.editor.text_color,
6241 font_family_name,
6242 font_family_id,
6243 font_id,
6244 font_size,
6245 font_properties,
6246 underline: Default::default(),
6247 },
6248 placeholder_text: None,
6249 theme,
6250 }
6251 };
6252
6253 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
6254 if let Some(highlighted) = style
6255 .text
6256 .clone()
6257 .highlight(highlight_style, font_cache)
6258 .log_err()
6259 {
6260 style.text = highlighted;
6261 }
6262 }
6263
6264 style
6265}
6266
6267trait SelectionExt {
6268 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
6269 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
6270 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
6271 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
6272 -> Range<u32>;
6273}
6274
6275impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
6276 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
6277 let start = self.start.to_point(buffer);
6278 let end = self.end.to_point(buffer);
6279 if self.reversed {
6280 end..start
6281 } else {
6282 start..end
6283 }
6284 }
6285
6286 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
6287 let start = self.start.to_offset(buffer);
6288 let end = self.end.to_offset(buffer);
6289 if self.reversed {
6290 end..start
6291 } else {
6292 start..end
6293 }
6294 }
6295
6296 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
6297 let start = self
6298 .start
6299 .to_point(&map.buffer_snapshot)
6300 .to_display_point(map);
6301 let end = self
6302 .end
6303 .to_point(&map.buffer_snapshot)
6304 .to_display_point(map);
6305 if self.reversed {
6306 end..start
6307 } else {
6308 start..end
6309 }
6310 }
6311
6312 fn spanned_rows(
6313 &self,
6314 include_end_if_at_line_start: bool,
6315 map: &DisplaySnapshot,
6316 ) -> Range<u32> {
6317 let start = self.start.to_point(&map.buffer_snapshot);
6318 let mut end = self.end.to_point(&map.buffer_snapshot);
6319 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
6320 end.row -= 1;
6321 }
6322
6323 let buffer_start = map.prev_line_boundary(start).0;
6324 let buffer_end = map.next_line_boundary(end).0;
6325 buffer_start.row..buffer_end.row + 1
6326 }
6327}
6328
6329impl<T: InvalidationRegion> InvalidationStack<T> {
6330 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
6331 where
6332 S: Clone + ToOffset,
6333 {
6334 while let Some(region) = self.last() {
6335 let all_selections_inside_invalidation_ranges =
6336 if selections.len() == region.ranges().len() {
6337 selections
6338 .iter()
6339 .zip(region.ranges().iter().map(|r| r.to_offset(&buffer)))
6340 .all(|(selection, invalidation_range)| {
6341 let head = selection.head().to_offset(&buffer);
6342 invalidation_range.start <= head && invalidation_range.end >= head
6343 })
6344 } else {
6345 false
6346 };
6347
6348 if all_selections_inside_invalidation_ranges {
6349 break;
6350 } else {
6351 self.pop();
6352 }
6353 }
6354 }
6355}
6356
6357impl<T> Default for InvalidationStack<T> {
6358 fn default() -> Self {
6359 Self(Default::default())
6360 }
6361}
6362
6363impl<T> Deref for InvalidationStack<T> {
6364 type Target = Vec<T>;
6365
6366 fn deref(&self) -> &Self::Target {
6367 &self.0
6368 }
6369}
6370
6371impl<T> DerefMut for InvalidationStack<T> {
6372 fn deref_mut(&mut self) -> &mut Self::Target {
6373 &mut self.0
6374 }
6375}
6376
6377impl InvalidationRegion for BracketPairState {
6378 fn ranges(&self) -> &[Range<Anchor>] {
6379 &self.ranges
6380 }
6381}
6382
6383impl InvalidationRegion for SnippetState {
6384 fn ranges(&self) -> &[Range<Anchor>] {
6385 &self.ranges[self.active_index]
6386 }
6387}
6388
6389impl Deref for EditorStyle {
6390 type Target = theme::Editor;
6391
6392 fn deref(&self) -> &Self::Target {
6393 &self.theme
6394 }
6395}
6396
6397pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
6398 let mut highlighted_lines = Vec::new();
6399 for line in diagnostic.message.lines() {
6400 highlighted_lines.push(highlight_diagnostic_message(line));
6401 }
6402
6403 Arc::new(move |cx: &mut BlockContext| {
6404 let settings = cx.global::<Settings>();
6405 let theme = &settings.theme.editor;
6406 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
6407 let font_size = (style.text_scale_factor * settings.buffer_font_size).round();
6408 Flex::column()
6409 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
6410 Label::new(
6411 line.clone(),
6412 style.message.clone().with_font_size(font_size),
6413 )
6414 .with_highlights(highlights.clone())
6415 .contained()
6416 .with_margin_left(cx.anchor_x)
6417 .boxed()
6418 }))
6419 .aligned()
6420 .left()
6421 .boxed()
6422 })
6423}
6424
6425pub fn highlight_diagnostic_message(message: &str) -> (String, Vec<usize>) {
6426 let mut message_without_backticks = String::new();
6427 let mut prev_offset = 0;
6428 let mut inside_block = false;
6429 let mut highlights = Vec::new();
6430 for (match_ix, (offset, _)) in message
6431 .match_indices('`')
6432 .chain([(message.len(), "")])
6433 .enumerate()
6434 {
6435 message_without_backticks.push_str(&message[prev_offset..offset]);
6436 if inside_block {
6437 highlights.extend(prev_offset - match_ix..offset - match_ix);
6438 }
6439
6440 inside_block = !inside_block;
6441 prev_offset = offset + 1;
6442 }
6443
6444 (message_without_backticks, highlights)
6445}
6446
6447pub fn diagnostic_style(
6448 severity: DiagnosticSeverity,
6449 valid: bool,
6450 theme: &theme::Editor,
6451) -> DiagnosticStyle {
6452 match (severity, valid) {
6453 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
6454 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
6455 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
6456 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
6457 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
6458 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
6459 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
6460 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
6461 _ => theme.invalid_hint_diagnostic.clone(),
6462 }
6463}
6464
6465pub fn combine_syntax_and_fuzzy_match_highlights(
6466 text: &str,
6467 default_style: HighlightStyle,
6468 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
6469 match_indices: &[usize],
6470) -> Vec<(Range<usize>, HighlightStyle)> {
6471 let mut result = Vec::new();
6472 let mut match_indices = match_indices.iter().copied().peekable();
6473
6474 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
6475 {
6476 syntax_highlight.weight = None;
6477
6478 // Add highlights for any fuzzy match characters before the next
6479 // syntax highlight range.
6480 while let Some(&match_index) = match_indices.peek() {
6481 if match_index >= range.start {
6482 break;
6483 }
6484 match_indices.next();
6485 let end_index = char_ix_after(match_index, text);
6486 let mut match_style = default_style;
6487 match_style.weight = Some(fonts::Weight::BOLD);
6488 result.push((match_index..end_index, match_style));
6489 }
6490
6491 if range.start == usize::MAX {
6492 break;
6493 }
6494
6495 // Add highlights for any fuzzy match characters within the
6496 // syntax highlight range.
6497 let mut offset = range.start;
6498 while let Some(&match_index) = match_indices.peek() {
6499 if match_index >= range.end {
6500 break;
6501 }
6502
6503 match_indices.next();
6504 if match_index > offset {
6505 result.push((offset..match_index, syntax_highlight));
6506 }
6507
6508 let mut end_index = char_ix_after(match_index, text);
6509 while let Some(&next_match_index) = match_indices.peek() {
6510 if next_match_index == end_index && next_match_index < range.end {
6511 end_index = char_ix_after(next_match_index, text);
6512 match_indices.next();
6513 } else {
6514 break;
6515 }
6516 }
6517
6518 let mut match_style = syntax_highlight;
6519 match_style.weight = Some(fonts::Weight::BOLD);
6520 result.push((match_index..end_index, match_style));
6521 offset = end_index;
6522 }
6523
6524 if offset < range.end {
6525 result.push((offset..range.end, syntax_highlight));
6526 }
6527 }
6528
6529 fn char_ix_after(ix: usize, text: &str) -> usize {
6530 ix + text[ix..].chars().next().unwrap().len_utf8()
6531 }
6532
6533 result
6534}
6535
6536pub fn styled_runs_for_code_label<'a>(
6537 label: &'a CodeLabel,
6538 syntax_theme: &'a theme::SyntaxTheme,
6539) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
6540 let fade_out = HighlightStyle {
6541 fade_out: Some(0.35),
6542 ..Default::default()
6543 };
6544
6545 let mut prev_end = label.filter_range.end;
6546 label
6547 .runs
6548 .iter()
6549 .enumerate()
6550 .flat_map(move |(ix, (range, highlight_id))| {
6551 let style = if let Some(style) = highlight_id.style(syntax_theme) {
6552 style
6553 } else {
6554 return Default::default();
6555 };
6556 let mut muted_style = style.clone();
6557 muted_style.highlight(fade_out);
6558
6559 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
6560 if range.start >= label.filter_range.end {
6561 if range.start > prev_end {
6562 runs.push((prev_end..range.start, fade_out));
6563 }
6564 runs.push((range.clone(), muted_style));
6565 } else if range.end <= label.filter_range.end {
6566 runs.push((range.clone(), style));
6567 } else {
6568 runs.push((range.start..label.filter_range.end, style));
6569 runs.push((label.filter_range.end..range.end, muted_style));
6570 }
6571 prev_end = cmp::max(prev_end, range.end);
6572
6573 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
6574 runs.push((prev_end..label.text.len(), fade_out));
6575 }
6576
6577 runs
6578 })
6579}
6580
6581#[cfg(test)]
6582mod tests {
6583 use crate::test::{
6584 assert_text_with_selections, build_editor, select_ranges, EditorLspTestContext,
6585 EditorTestContext,
6586 };
6587
6588 use super::*;
6589 use futures::StreamExt;
6590 use gpui::{
6591 geometry::rect::RectF,
6592 platform::{WindowBounds, WindowOptions},
6593 };
6594 use indoc::indoc;
6595 use language::{FakeLspAdapter, LanguageConfig};
6596 use project::FakeFs;
6597 use settings::EditorSettings;
6598 use std::{cell::RefCell, rc::Rc, time::Instant};
6599 use text::Point;
6600 use unindent::Unindent;
6601 use util::{
6602 assert_set_eq,
6603 test::{
6604 marked_text_by, marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker,
6605 },
6606 };
6607 use workspace::{FollowableItem, ItemHandle, NavigationEntry, Pane};
6608
6609 #[gpui::test]
6610 fn test_edit_events(cx: &mut MutableAppContext) {
6611 cx.set_global(Settings::test(cx));
6612 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6613
6614 let events = Rc::new(RefCell::new(Vec::new()));
6615 let (_, editor1) = cx.add_window(Default::default(), {
6616 let events = events.clone();
6617 |cx| {
6618 cx.subscribe(&cx.handle(), move |_, _, event, _| {
6619 if matches!(
6620 event,
6621 Event::Edited | Event::BufferEdited | Event::DirtyChanged
6622 ) {
6623 events.borrow_mut().push(("editor1", *event));
6624 }
6625 })
6626 .detach();
6627 Editor::for_buffer(buffer.clone(), None, cx)
6628 }
6629 });
6630 let (_, editor2) = cx.add_window(Default::default(), {
6631 let events = events.clone();
6632 |cx| {
6633 cx.subscribe(&cx.handle(), move |_, _, event, _| {
6634 if matches!(
6635 event,
6636 Event::Edited | Event::BufferEdited | Event::DirtyChanged
6637 ) {
6638 events.borrow_mut().push(("editor2", *event));
6639 }
6640 })
6641 .detach();
6642 Editor::for_buffer(buffer.clone(), None, cx)
6643 }
6644 });
6645 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6646
6647 // Mutating editor 1 will emit an `Edited` event only for that editor.
6648 editor1.update(cx, |editor, cx| editor.insert("X", cx));
6649 assert_eq!(
6650 mem::take(&mut *events.borrow_mut()),
6651 [
6652 ("editor1", Event::Edited),
6653 ("editor1", Event::BufferEdited),
6654 ("editor2", Event::BufferEdited),
6655 ("editor1", Event::DirtyChanged),
6656 ("editor2", Event::DirtyChanged)
6657 ]
6658 );
6659
6660 // Mutating editor 2 will emit an `Edited` event only for that editor.
6661 editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
6662 assert_eq!(
6663 mem::take(&mut *events.borrow_mut()),
6664 [
6665 ("editor2", Event::Edited),
6666 ("editor1", Event::BufferEdited),
6667 ("editor2", Event::BufferEdited),
6668 ]
6669 );
6670
6671 // Undoing on editor 1 will emit an `Edited` event only for that editor.
6672 editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
6673 assert_eq!(
6674 mem::take(&mut *events.borrow_mut()),
6675 [
6676 ("editor1", Event::Edited),
6677 ("editor1", Event::BufferEdited),
6678 ("editor2", Event::BufferEdited),
6679 ("editor1", Event::DirtyChanged),
6680 ("editor2", Event::DirtyChanged),
6681 ]
6682 );
6683
6684 // Redoing on editor 1 will emit an `Edited` event only for that editor.
6685 editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
6686 assert_eq!(
6687 mem::take(&mut *events.borrow_mut()),
6688 [
6689 ("editor1", Event::Edited),
6690 ("editor1", Event::BufferEdited),
6691 ("editor2", Event::BufferEdited),
6692 ("editor1", Event::DirtyChanged),
6693 ("editor2", Event::DirtyChanged),
6694 ]
6695 );
6696
6697 // Undoing on editor 2 will emit an `Edited` event only for that editor.
6698 editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
6699 assert_eq!(
6700 mem::take(&mut *events.borrow_mut()),
6701 [
6702 ("editor2", Event::Edited),
6703 ("editor1", Event::BufferEdited),
6704 ("editor2", Event::BufferEdited),
6705 ("editor1", Event::DirtyChanged),
6706 ("editor2", Event::DirtyChanged),
6707 ]
6708 );
6709
6710 // Redoing on editor 2 will emit an `Edited` event only for that editor.
6711 editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
6712 assert_eq!(
6713 mem::take(&mut *events.borrow_mut()),
6714 [
6715 ("editor2", Event::Edited),
6716 ("editor1", Event::BufferEdited),
6717 ("editor2", Event::BufferEdited),
6718 ("editor1", Event::DirtyChanged),
6719 ("editor2", Event::DirtyChanged),
6720 ]
6721 );
6722
6723 // No event is emitted when the mutation is a no-op.
6724 editor2.update(cx, |editor, cx| {
6725 editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
6726
6727 editor.backspace(&Backspace, cx);
6728 });
6729 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6730 }
6731
6732 #[gpui::test]
6733 fn test_undo_redo_with_selection_restoration(cx: &mut MutableAppContext) {
6734 cx.set_global(Settings::test(cx));
6735 let mut now = Instant::now();
6736 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6737 let group_interval = buffer.read(cx).transaction_group_interval();
6738 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
6739 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6740
6741 editor.update(cx, |editor, cx| {
6742 editor.start_transaction_at(now, cx);
6743 editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
6744
6745 editor.insert("cd", cx);
6746 editor.end_transaction_at(now, cx);
6747 assert_eq!(editor.text(cx), "12cd56");
6748 assert_eq!(editor.selections.ranges(cx), vec![4..4]);
6749
6750 editor.start_transaction_at(now, cx);
6751 editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
6752 editor.insert("e", cx);
6753 editor.end_transaction_at(now, cx);
6754 assert_eq!(editor.text(cx), "12cde6");
6755 assert_eq!(editor.selections.ranges(cx), vec![5..5]);
6756
6757 now += group_interval + Duration::from_millis(1);
6758 editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
6759
6760 // Simulate an edit in another editor
6761 buffer.update(cx, |buffer, cx| {
6762 buffer.start_transaction_at(now, cx);
6763 buffer.edit([(0..1, "a")], None, cx);
6764 buffer.edit([(1..1, "b")], None, cx);
6765 buffer.end_transaction_at(now, cx);
6766 });
6767
6768 assert_eq!(editor.text(cx), "ab2cde6");
6769 assert_eq!(editor.selections.ranges(cx), vec![3..3]);
6770
6771 // Last transaction happened past the group interval in a different editor.
6772 // Undo it individually and don't restore selections.
6773 editor.undo(&Undo, cx);
6774 assert_eq!(editor.text(cx), "12cde6");
6775 assert_eq!(editor.selections.ranges(cx), vec![2..2]);
6776
6777 // First two transactions happened within the group interval in this editor.
6778 // Undo them together and restore selections.
6779 editor.undo(&Undo, cx);
6780 editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
6781 assert_eq!(editor.text(cx), "123456");
6782 assert_eq!(editor.selections.ranges(cx), vec![0..0]);
6783
6784 // Redo the first two transactions together.
6785 editor.redo(&Redo, cx);
6786 assert_eq!(editor.text(cx), "12cde6");
6787 assert_eq!(editor.selections.ranges(cx), vec![5..5]);
6788
6789 // Redo the last transaction on its own.
6790 editor.redo(&Redo, cx);
6791 assert_eq!(editor.text(cx), "ab2cde6");
6792 assert_eq!(editor.selections.ranges(cx), vec![6..6]);
6793
6794 // Test empty transactions.
6795 editor.start_transaction_at(now, cx);
6796 editor.end_transaction_at(now, cx);
6797 editor.undo(&Undo, cx);
6798 assert_eq!(editor.text(cx), "12cde6");
6799 });
6800 }
6801
6802 #[gpui::test]
6803 fn test_ime_composition(cx: &mut MutableAppContext) {
6804 cx.set_global(Settings::test(cx));
6805 let buffer = cx.add_model(|cx| {
6806 let mut buffer = language::Buffer::new(0, "abcde", cx);
6807 // Ensure automatic grouping doesn't occur.
6808 buffer.set_group_interval(Duration::ZERO);
6809 buffer
6810 });
6811
6812 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
6813 cx.add_window(Default::default(), |cx| {
6814 let mut editor = build_editor(buffer.clone(), cx);
6815
6816 // Start a new IME composition.
6817 editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
6818 editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
6819 editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
6820 assert_eq!(editor.text(cx), "äbcde");
6821 assert_eq!(
6822 editor.marked_text_ranges(cx),
6823 Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
6824 );
6825
6826 // Finalize IME composition.
6827 editor.replace_text_in_range(None, "ā", cx);
6828 assert_eq!(editor.text(cx), "ābcde");
6829 assert_eq!(editor.marked_text_ranges(cx), None);
6830
6831 // IME composition edits are grouped and are undone/redone at once.
6832 editor.undo(&Default::default(), cx);
6833 assert_eq!(editor.text(cx), "abcde");
6834 assert_eq!(editor.marked_text_ranges(cx), None);
6835 editor.redo(&Default::default(), cx);
6836 assert_eq!(editor.text(cx), "ābcde");
6837 assert_eq!(editor.marked_text_ranges(cx), None);
6838
6839 // Start a new IME composition.
6840 editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
6841 assert_eq!(
6842 editor.marked_text_ranges(cx),
6843 Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
6844 );
6845
6846 // Undoing during an IME composition cancels it.
6847 editor.undo(&Default::default(), cx);
6848 assert_eq!(editor.text(cx), "ābcde");
6849 assert_eq!(editor.marked_text_ranges(cx), None);
6850
6851 // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
6852 editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
6853 assert_eq!(editor.text(cx), "ābcdè");
6854 assert_eq!(
6855 editor.marked_text_ranges(cx),
6856 Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
6857 );
6858
6859 // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
6860 editor.replace_text_in_range(Some(4..999), "ę", cx);
6861 assert_eq!(editor.text(cx), "ābcdę");
6862 assert_eq!(editor.marked_text_ranges(cx), None);
6863
6864 // Start a new IME composition with multiple cursors.
6865 editor.change_selections(None, cx, |s| {
6866 s.select_ranges([
6867 OffsetUtf16(1)..OffsetUtf16(1),
6868 OffsetUtf16(3)..OffsetUtf16(3),
6869 OffsetUtf16(5)..OffsetUtf16(5),
6870 ])
6871 });
6872 editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
6873 assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
6874 assert_eq!(
6875 editor.marked_text_ranges(cx),
6876 Some(vec![
6877 OffsetUtf16(0)..OffsetUtf16(3),
6878 OffsetUtf16(4)..OffsetUtf16(7),
6879 OffsetUtf16(8)..OffsetUtf16(11)
6880 ])
6881 );
6882
6883 // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
6884 editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
6885 assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
6886 assert_eq!(
6887 editor.marked_text_ranges(cx),
6888 Some(vec![
6889 OffsetUtf16(1)..OffsetUtf16(2),
6890 OffsetUtf16(5)..OffsetUtf16(6),
6891 OffsetUtf16(9)..OffsetUtf16(10)
6892 ])
6893 );
6894
6895 // Finalize IME composition with multiple cursors.
6896 editor.replace_text_in_range(Some(9..10), "2", cx);
6897 assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
6898 assert_eq!(editor.marked_text_ranges(cx), None);
6899
6900 editor
6901 });
6902 }
6903
6904 #[gpui::test]
6905 fn test_selection_with_mouse(cx: &mut gpui::MutableAppContext) {
6906 cx.set_global(Settings::test(cx));
6907
6908 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
6909 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6910 editor.update(cx, |view, cx| {
6911 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
6912 });
6913 assert_eq!(
6914 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6915 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
6916 );
6917
6918 editor.update(cx, |view, cx| {
6919 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6920 });
6921
6922 assert_eq!(
6923 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6924 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6925 );
6926
6927 editor.update(cx, |view, cx| {
6928 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6929 });
6930
6931 assert_eq!(
6932 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6933 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
6934 );
6935
6936 editor.update(cx, |view, cx| {
6937 view.end_selection(cx);
6938 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6939 });
6940
6941 assert_eq!(
6942 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6943 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
6944 );
6945
6946 editor.update(cx, |view, cx| {
6947 view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx);
6948 view.update_selection(DisplayPoint::new(0, 0), 0, Vector2F::zero(), cx);
6949 });
6950
6951 assert_eq!(
6952 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6953 [
6954 DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
6955 DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
6956 ]
6957 );
6958
6959 editor.update(cx, |view, cx| {
6960 view.end_selection(cx);
6961 });
6962
6963 assert_eq!(
6964 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6965 [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
6966 );
6967 }
6968
6969 #[gpui::test]
6970 fn test_canceling_pending_selection(cx: &mut gpui::MutableAppContext) {
6971 cx.set_global(Settings::test(cx));
6972 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
6973 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6974
6975 view.update(cx, |view, cx| {
6976 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
6977 assert_eq!(
6978 view.selections.display_ranges(cx),
6979 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
6980 );
6981 });
6982
6983 view.update(cx, |view, cx| {
6984 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6985 assert_eq!(
6986 view.selections.display_ranges(cx),
6987 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6988 );
6989 });
6990
6991 view.update(cx, |view, cx| {
6992 view.cancel(&Cancel, cx);
6993 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6994 assert_eq!(
6995 view.selections.display_ranges(cx),
6996 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6997 );
6998 });
6999 }
7000
7001 #[gpui::test]
7002 fn test_clone(cx: &mut gpui::MutableAppContext) {
7003 let (text, selection_ranges) = marked_text_ranges(indoc! {"
7004 one
7005 two
7006 three[]
7007 four
7008 five[]
7009 "});
7010 cx.set_global(Settings::test(cx));
7011 let buffer = MultiBuffer::build_simple(&text, cx);
7012
7013 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7014
7015 editor.update(cx, |editor, cx| {
7016 editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
7017 editor.fold_ranges(
7018 [
7019 Point::new(1, 0)..Point::new(2, 0),
7020 Point::new(3, 0)..Point::new(4, 0),
7021 ],
7022 cx,
7023 );
7024 });
7025
7026 let (_, cloned_editor) = editor.update(cx, |editor, cx| {
7027 cx.add_window(Default::default(), |cx| editor.clone(cx))
7028 });
7029
7030 let snapshot = editor.update(cx, |e, cx| e.snapshot(cx));
7031 let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx));
7032
7033 assert_eq!(
7034 cloned_editor.update(cx, |e, cx| e.display_text(cx)),
7035 editor.update(cx, |e, cx| e.display_text(cx))
7036 );
7037 assert_eq!(
7038 cloned_snapshot
7039 .folds_in_range(0..text.len())
7040 .collect::<Vec<_>>(),
7041 snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
7042 );
7043 assert_set_eq!(
7044 cloned_editor.read(cx).selections.ranges::<Point>(cx),
7045 editor.read(cx).selections.ranges(cx)
7046 );
7047 assert_set_eq!(
7048 cloned_editor.update(cx, |e, cx| e.selections.display_ranges(cx)),
7049 editor.update(cx, |e, cx| e.selections.display_ranges(cx))
7050 );
7051 }
7052
7053 #[gpui::test]
7054 fn test_navigation_history(cx: &mut gpui::MutableAppContext) {
7055 cx.set_global(Settings::test(cx));
7056 use workspace::Item;
7057 let pane = cx.add_view(Default::default(), |cx| Pane::new(cx));
7058 let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
7059
7060 cx.add_window(Default::default(), |cx| {
7061 let mut editor = build_editor(buffer.clone(), cx);
7062 let handle = cx.handle();
7063 editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
7064
7065 fn pop_history(
7066 editor: &mut Editor,
7067 cx: &mut MutableAppContext,
7068 ) -> Option<NavigationEntry> {
7069 editor.nav_history.as_mut().unwrap().pop_backward(cx)
7070 }
7071
7072 // Move the cursor a small distance.
7073 // Nothing is added to the navigation history.
7074 editor.change_selections(None, cx, |s| {
7075 s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)])
7076 });
7077 editor.change_selections(None, cx, |s| {
7078 s.select_display_ranges([DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)])
7079 });
7080 assert!(pop_history(&mut editor, cx).is_none());
7081
7082 // Move the cursor a large distance.
7083 // The history can jump back to the previous position.
7084 editor.change_selections(None, cx, |s| {
7085 s.select_display_ranges([DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)])
7086 });
7087 let nav_entry = pop_history(&mut editor, cx).unwrap();
7088 editor.navigate(nav_entry.data.unwrap(), cx);
7089 assert_eq!(nav_entry.item.id(), cx.view_id());
7090 assert_eq!(
7091 editor.selections.display_ranges(cx),
7092 &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]
7093 );
7094 assert!(pop_history(&mut editor, cx).is_none());
7095
7096 // Move the cursor a small distance via the mouse.
7097 // Nothing is added to the navigation history.
7098 editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx);
7099 editor.end_selection(cx);
7100 assert_eq!(
7101 editor.selections.display_ranges(cx),
7102 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
7103 );
7104 assert!(pop_history(&mut editor, cx).is_none());
7105
7106 // Move the cursor a large distance via the mouse.
7107 // The history can jump back to the previous position.
7108 editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx);
7109 editor.end_selection(cx);
7110 assert_eq!(
7111 editor.selections.display_ranges(cx),
7112 &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]
7113 );
7114 let nav_entry = pop_history(&mut editor, cx).unwrap();
7115 editor.navigate(nav_entry.data.unwrap(), cx);
7116 assert_eq!(nav_entry.item.id(), cx.view_id());
7117 assert_eq!(
7118 editor.selections.display_ranges(cx),
7119 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
7120 );
7121 assert!(pop_history(&mut editor, cx).is_none());
7122
7123 // Set scroll position to check later
7124 editor.set_scroll_position(Vector2F::new(5.5, 5.5), cx);
7125 let original_scroll_position = editor.scroll_position;
7126 let original_scroll_top_anchor = editor.scroll_top_anchor.clone();
7127
7128 // Jump to the end of the document and adjust scroll
7129 editor.move_to_end(&MoveToEnd, cx);
7130 editor.set_scroll_position(Vector2F::new(-2.5, -0.5), cx);
7131 assert_ne!(editor.scroll_position, original_scroll_position);
7132 assert_ne!(editor.scroll_top_anchor, original_scroll_top_anchor);
7133
7134 let nav_entry = pop_history(&mut editor, cx).unwrap();
7135 editor.navigate(nav_entry.data.unwrap(), cx);
7136 assert_eq!(editor.scroll_position, original_scroll_position);
7137 assert_eq!(editor.scroll_top_anchor, original_scroll_top_anchor);
7138
7139 // Ensure we don't panic when navigation data contains invalid anchors *and* points.
7140 let mut invalid_anchor = editor.scroll_top_anchor.clone();
7141 invalid_anchor.text_anchor.buffer_id = Some(999);
7142 let invalid_point = Point::new(9999, 0);
7143 editor.navigate(
7144 Box::new(NavigationData {
7145 cursor_anchor: invalid_anchor.clone(),
7146 cursor_position: invalid_point,
7147 scroll_top_anchor: invalid_anchor.clone(),
7148 scroll_top_row: invalid_point.row,
7149 scroll_position: Default::default(),
7150 }),
7151 cx,
7152 );
7153 assert_eq!(
7154 editor.selections.display_ranges(cx),
7155 &[editor.max_point(cx)..editor.max_point(cx)]
7156 );
7157 assert_eq!(
7158 editor.scroll_position(cx),
7159 vec2f(0., editor.max_point(cx).row() as f32)
7160 );
7161
7162 editor
7163 });
7164 }
7165
7166 #[gpui::test]
7167 fn test_cancel(cx: &mut gpui::MutableAppContext) {
7168 cx.set_global(Settings::test(cx));
7169 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
7170 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7171
7172 view.update(cx, |view, cx| {
7173 view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
7174 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
7175 view.end_selection(cx);
7176
7177 view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx);
7178 view.update_selection(DisplayPoint::new(0, 3), 0, Vector2F::zero(), cx);
7179 view.end_selection(cx);
7180 assert_eq!(
7181 view.selections.display_ranges(cx),
7182 [
7183 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
7184 DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1),
7185 ]
7186 );
7187 });
7188
7189 view.update(cx, |view, cx| {
7190 view.cancel(&Cancel, cx);
7191 assert_eq!(
7192 view.selections.display_ranges(cx),
7193 [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)]
7194 );
7195 });
7196
7197 view.update(cx, |view, cx| {
7198 view.cancel(&Cancel, cx);
7199 assert_eq!(
7200 view.selections.display_ranges(cx),
7201 [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)]
7202 );
7203 });
7204 }
7205
7206 #[gpui::test]
7207 fn test_fold(cx: &mut gpui::MutableAppContext) {
7208 cx.set_global(Settings::test(cx));
7209 let buffer = MultiBuffer::build_simple(
7210 &"
7211 impl Foo {
7212 // Hello!
7213
7214 fn a() {
7215 1
7216 }
7217
7218 fn b() {
7219 2
7220 }
7221
7222 fn c() {
7223 3
7224 }
7225 }
7226 "
7227 .unindent(),
7228 cx,
7229 );
7230 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7231
7232 view.update(cx, |view, cx| {
7233 view.change_selections(None, cx, |s| {
7234 s.select_display_ranges([DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)]);
7235 });
7236 view.fold(&Fold, cx);
7237 assert_eq!(
7238 view.display_text(cx),
7239 "
7240 impl Foo {
7241 // Hello!
7242
7243 fn a() {
7244 1
7245 }
7246
7247 fn b() {…
7248 }
7249
7250 fn c() {…
7251 }
7252 }
7253 "
7254 .unindent(),
7255 );
7256
7257 view.fold(&Fold, cx);
7258 assert_eq!(
7259 view.display_text(cx),
7260 "
7261 impl Foo {…
7262 }
7263 "
7264 .unindent(),
7265 );
7266
7267 view.unfold_lines(&UnfoldLines, cx);
7268 assert_eq!(
7269 view.display_text(cx),
7270 "
7271 impl Foo {
7272 // Hello!
7273
7274 fn a() {
7275 1
7276 }
7277
7278 fn b() {…
7279 }
7280
7281 fn c() {…
7282 }
7283 }
7284 "
7285 .unindent(),
7286 );
7287
7288 view.unfold_lines(&UnfoldLines, cx);
7289 assert_eq!(view.display_text(cx), buffer.read(cx).read(cx).text());
7290 });
7291 }
7292
7293 #[gpui::test]
7294 fn test_move_cursor(cx: &mut gpui::MutableAppContext) {
7295 cx.set_global(Settings::test(cx));
7296 let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
7297 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7298
7299 buffer.update(cx, |buffer, cx| {
7300 buffer.edit(
7301 vec![
7302 (Point::new(1, 0)..Point::new(1, 0), "\t"),
7303 (Point::new(1, 1)..Point::new(1, 1), "\t"),
7304 ],
7305 None,
7306 cx,
7307 );
7308 });
7309
7310 view.update(cx, |view, cx| {
7311 assert_eq!(
7312 view.selections.display_ranges(cx),
7313 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
7314 );
7315
7316 view.move_down(&MoveDown, cx);
7317 assert_eq!(
7318 view.selections.display_ranges(cx),
7319 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
7320 );
7321
7322 view.move_right(&MoveRight, cx);
7323 assert_eq!(
7324 view.selections.display_ranges(cx),
7325 &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
7326 );
7327
7328 view.move_left(&MoveLeft, cx);
7329 assert_eq!(
7330 view.selections.display_ranges(cx),
7331 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
7332 );
7333
7334 view.move_up(&MoveUp, cx);
7335 assert_eq!(
7336 view.selections.display_ranges(cx),
7337 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
7338 );
7339
7340 view.move_to_end(&MoveToEnd, cx);
7341 assert_eq!(
7342 view.selections.display_ranges(cx),
7343 &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)]
7344 );
7345
7346 view.move_to_beginning(&MoveToBeginning, cx);
7347 assert_eq!(
7348 view.selections.display_ranges(cx),
7349 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
7350 );
7351
7352 view.change_selections(None, cx, |s| {
7353 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]);
7354 });
7355 view.select_to_beginning(&SelectToBeginning, cx);
7356 assert_eq!(
7357 view.selections.display_ranges(cx),
7358 &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)]
7359 );
7360
7361 view.select_to_end(&SelectToEnd, cx);
7362 assert_eq!(
7363 view.selections.display_ranges(cx),
7364 &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)]
7365 );
7366 });
7367 }
7368
7369 #[gpui::test]
7370 fn test_move_cursor_multibyte(cx: &mut gpui::MutableAppContext) {
7371 cx.set_global(Settings::test(cx));
7372 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx);
7373 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7374
7375 assert_eq!('ⓐ'.len_utf8(), 3);
7376 assert_eq!('α'.len_utf8(), 2);
7377
7378 view.update(cx, |view, cx| {
7379 view.fold_ranges(
7380 vec![
7381 Point::new(0, 6)..Point::new(0, 12),
7382 Point::new(1, 2)..Point::new(1, 4),
7383 Point::new(2, 4)..Point::new(2, 8),
7384 ],
7385 cx,
7386 );
7387 assert_eq!(view.display_text(cx), "ⓐⓑ…ⓔ\nab…e\nαβ…ε\n");
7388
7389 view.move_right(&MoveRight, cx);
7390 assert_eq!(
7391 view.selections.display_ranges(cx),
7392 &[empty_range(0, "ⓐ".len())]
7393 );
7394 view.move_right(&MoveRight, cx);
7395 assert_eq!(
7396 view.selections.display_ranges(cx),
7397 &[empty_range(0, "ⓐⓑ".len())]
7398 );
7399 view.move_right(&MoveRight, cx);
7400 assert_eq!(
7401 view.selections.display_ranges(cx),
7402 &[empty_range(0, "ⓐⓑ…".len())]
7403 );
7404
7405 view.move_down(&MoveDown, cx);
7406 assert_eq!(
7407 view.selections.display_ranges(cx),
7408 &[empty_range(1, "ab…".len())]
7409 );
7410 view.move_left(&MoveLeft, cx);
7411 assert_eq!(
7412 view.selections.display_ranges(cx),
7413 &[empty_range(1, "ab".len())]
7414 );
7415 view.move_left(&MoveLeft, cx);
7416 assert_eq!(
7417 view.selections.display_ranges(cx),
7418 &[empty_range(1, "a".len())]
7419 );
7420
7421 view.move_down(&MoveDown, cx);
7422 assert_eq!(
7423 view.selections.display_ranges(cx),
7424 &[empty_range(2, "α".len())]
7425 );
7426 view.move_right(&MoveRight, cx);
7427 assert_eq!(
7428 view.selections.display_ranges(cx),
7429 &[empty_range(2, "αβ".len())]
7430 );
7431 view.move_right(&MoveRight, cx);
7432 assert_eq!(
7433 view.selections.display_ranges(cx),
7434 &[empty_range(2, "αβ…".len())]
7435 );
7436 view.move_right(&MoveRight, cx);
7437 assert_eq!(
7438 view.selections.display_ranges(cx),
7439 &[empty_range(2, "αβ…ε".len())]
7440 );
7441
7442 view.move_up(&MoveUp, cx);
7443 assert_eq!(
7444 view.selections.display_ranges(cx),
7445 &[empty_range(1, "ab…e".len())]
7446 );
7447 view.move_up(&MoveUp, cx);
7448 assert_eq!(
7449 view.selections.display_ranges(cx),
7450 &[empty_range(0, "ⓐⓑ…ⓔ".len())]
7451 );
7452 view.move_left(&MoveLeft, cx);
7453 assert_eq!(
7454 view.selections.display_ranges(cx),
7455 &[empty_range(0, "ⓐⓑ…".len())]
7456 );
7457 view.move_left(&MoveLeft, cx);
7458 assert_eq!(
7459 view.selections.display_ranges(cx),
7460 &[empty_range(0, "ⓐⓑ".len())]
7461 );
7462 view.move_left(&MoveLeft, cx);
7463 assert_eq!(
7464 view.selections.display_ranges(cx),
7465 &[empty_range(0, "ⓐ".len())]
7466 );
7467 });
7468 }
7469
7470 #[gpui::test]
7471 fn test_move_cursor_different_line_lengths(cx: &mut gpui::MutableAppContext) {
7472 cx.set_global(Settings::test(cx));
7473 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
7474 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7475 view.update(cx, |view, cx| {
7476 view.change_selections(None, cx, |s| {
7477 s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
7478 });
7479 view.move_down(&MoveDown, cx);
7480 assert_eq!(
7481 view.selections.display_ranges(cx),
7482 &[empty_range(1, "abcd".len())]
7483 );
7484
7485 view.move_down(&MoveDown, cx);
7486 assert_eq!(
7487 view.selections.display_ranges(cx),
7488 &[empty_range(2, "αβγ".len())]
7489 );
7490
7491 view.move_down(&MoveDown, cx);
7492 assert_eq!(
7493 view.selections.display_ranges(cx),
7494 &[empty_range(3, "abcd".len())]
7495 );
7496
7497 view.move_down(&MoveDown, cx);
7498 assert_eq!(
7499 view.selections.display_ranges(cx),
7500 &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
7501 );
7502
7503 view.move_up(&MoveUp, cx);
7504 assert_eq!(
7505 view.selections.display_ranges(cx),
7506 &[empty_range(3, "abcd".len())]
7507 );
7508
7509 view.move_up(&MoveUp, cx);
7510 assert_eq!(
7511 view.selections.display_ranges(cx),
7512 &[empty_range(2, "αβγ".len())]
7513 );
7514 });
7515 }
7516
7517 #[gpui::test]
7518 fn test_beginning_end_of_line(cx: &mut gpui::MutableAppContext) {
7519 cx.set_global(Settings::test(cx));
7520 let buffer = MultiBuffer::build_simple("abc\n def", cx);
7521 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7522 view.update(cx, |view, cx| {
7523 view.change_selections(None, cx, |s| {
7524 s.select_display_ranges([
7525 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7526 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
7527 ]);
7528 });
7529 });
7530
7531 view.update(cx, |view, cx| {
7532 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7533 assert_eq!(
7534 view.selections.display_ranges(cx),
7535 &[
7536 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7537 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7538 ]
7539 );
7540 });
7541
7542 view.update(cx, |view, cx| {
7543 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7544 assert_eq!(
7545 view.selections.display_ranges(cx),
7546 &[
7547 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7548 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7549 ]
7550 );
7551 });
7552
7553 view.update(cx, |view, cx| {
7554 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7555 assert_eq!(
7556 view.selections.display_ranges(cx),
7557 &[
7558 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7559 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7560 ]
7561 );
7562 });
7563
7564 view.update(cx, |view, cx| {
7565 view.move_to_end_of_line(&MoveToEndOfLine, cx);
7566 assert_eq!(
7567 view.selections.display_ranges(cx),
7568 &[
7569 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7570 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
7571 ]
7572 );
7573 });
7574
7575 // Moving to the end of line again is a no-op.
7576 view.update(cx, |view, cx| {
7577 view.move_to_end_of_line(&MoveToEndOfLine, cx);
7578 assert_eq!(
7579 view.selections.display_ranges(cx),
7580 &[
7581 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7582 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
7583 ]
7584 );
7585 });
7586
7587 view.update(cx, |view, cx| {
7588 view.move_left(&MoveLeft, cx);
7589 view.select_to_beginning_of_line(
7590 &SelectToBeginningOfLine {
7591 stop_at_soft_wraps: true,
7592 },
7593 cx,
7594 );
7595 assert_eq!(
7596 view.selections.display_ranges(cx),
7597 &[
7598 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7599 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7600 ]
7601 );
7602 });
7603
7604 view.update(cx, |view, cx| {
7605 view.select_to_beginning_of_line(
7606 &SelectToBeginningOfLine {
7607 stop_at_soft_wraps: true,
7608 },
7609 cx,
7610 );
7611 assert_eq!(
7612 view.selections.display_ranges(cx),
7613 &[
7614 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7615 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0),
7616 ]
7617 );
7618 });
7619
7620 view.update(cx, |view, cx| {
7621 view.select_to_beginning_of_line(
7622 &SelectToBeginningOfLine {
7623 stop_at_soft_wraps: true,
7624 },
7625 cx,
7626 );
7627 assert_eq!(
7628 view.selections.display_ranges(cx),
7629 &[
7630 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7631 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7632 ]
7633 );
7634 });
7635
7636 view.update(cx, |view, cx| {
7637 view.select_to_end_of_line(
7638 &SelectToEndOfLine {
7639 stop_at_soft_wraps: true,
7640 },
7641 cx,
7642 );
7643 assert_eq!(
7644 view.selections.display_ranges(cx),
7645 &[
7646 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
7647 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5),
7648 ]
7649 );
7650 });
7651
7652 view.update(cx, |view, cx| {
7653 view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
7654 assert_eq!(view.display_text(cx), "ab\n de");
7655 assert_eq!(
7656 view.selections.display_ranges(cx),
7657 &[
7658 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7659 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
7660 ]
7661 );
7662 });
7663
7664 view.update(cx, |view, cx| {
7665 view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
7666 assert_eq!(view.display_text(cx), "\n");
7667 assert_eq!(
7668 view.selections.display_ranges(cx),
7669 &[
7670 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7671 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7672 ]
7673 );
7674 });
7675 }
7676
7677 #[gpui::test]
7678 fn test_prev_next_word_boundary(cx: &mut gpui::MutableAppContext) {
7679 cx.set_global(Settings::test(cx));
7680 let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx);
7681 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7682 view.update(cx, |view, cx| {
7683 view.change_selections(None, cx, |s| {
7684 s.select_display_ranges([
7685 DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
7686 DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
7687 ])
7688 });
7689
7690 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7691 assert_selection_ranges(
7692 "use std::<>str::{foo, bar}\n\n {[]baz.qux()}",
7693 vec![('<', '>'), ('[', ']')],
7694 view,
7695 cx,
7696 );
7697
7698 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7699 assert_selection_ranges(
7700 "use std<>::str::{foo, bar}\n\n []{baz.qux()}",
7701 vec![('<', '>'), ('[', ']')],
7702 view,
7703 cx,
7704 );
7705
7706 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7707 assert_selection_ranges(
7708 "use <>std::str::{foo, bar}\n\n[] {baz.qux()}",
7709 vec![('<', '>'), ('[', ']')],
7710 view,
7711 cx,
7712 );
7713
7714 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7715 assert_selection_ranges(
7716 "<>use std::str::{foo, bar}\n[]\n {baz.qux()}",
7717 vec![('<', '>'), ('[', ']')],
7718 view,
7719 cx,
7720 );
7721
7722 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7723 assert_selection_ranges(
7724 "<>use std::str::{foo, bar[]}\n\n {baz.qux()}",
7725 vec![('<', '>'), ('[', ']')],
7726 view,
7727 cx,
7728 );
7729
7730 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7731 assert_selection_ranges(
7732 "use<> std::str::{foo, bar}[]\n\n {baz.qux()}",
7733 vec![('<', '>'), ('[', ']')],
7734 view,
7735 cx,
7736 );
7737
7738 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7739 assert_selection_ranges(
7740 "use std<>::str::{foo, bar}\n[]\n {baz.qux()}",
7741 vec![('<', '>'), ('[', ']')],
7742 view,
7743 cx,
7744 );
7745
7746 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7747 assert_selection_ranges(
7748 "use std::<>str::{foo, bar}\n\n {[]baz.qux()}",
7749 vec![('<', '>'), ('[', ']')],
7750 view,
7751 cx,
7752 );
7753
7754 view.move_right(&MoveRight, cx);
7755 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7756 assert_selection_ranges(
7757 "use std::>s<tr::{foo, bar}\n\n {]b[az.qux()}",
7758 vec![('<', '>'), ('[', ']')],
7759 view,
7760 cx,
7761 );
7762
7763 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7764 assert_selection_ranges(
7765 "use std>::s<tr::{foo, bar}\n\n ]{b[az.qux()}",
7766 vec![('<', '>'), ('[', ']')],
7767 view,
7768 cx,
7769 );
7770
7771 view.select_to_next_word_end(&SelectToNextWordEnd, cx);
7772 assert_selection_ranges(
7773 "use std::>s<tr::{foo, bar}\n\n {]b[az.qux()}",
7774 vec![('<', '>'), ('[', ']')],
7775 view,
7776 cx,
7777 );
7778 });
7779 }
7780
7781 #[gpui::test]
7782 fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut gpui::MutableAppContext) {
7783 cx.set_global(Settings::test(cx));
7784 let buffer = MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx);
7785 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7786
7787 view.update(cx, |view, cx| {
7788 view.set_wrap_width(Some(140.), cx);
7789 assert_eq!(
7790 view.display_text(cx),
7791 "use one::{\n two::three::\n four::five\n};"
7792 );
7793
7794 view.change_selections(None, cx, |s| {
7795 s.select_display_ranges([DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)]);
7796 });
7797
7798 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7799 assert_eq!(
7800 view.selections.display_ranges(cx),
7801 &[DisplayPoint::new(1, 9)..DisplayPoint::new(1, 9)]
7802 );
7803
7804 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7805 assert_eq!(
7806 view.selections.display_ranges(cx),
7807 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
7808 );
7809
7810 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7811 assert_eq!(
7812 view.selections.display_ranges(cx),
7813 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7814 );
7815
7816 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7817 assert_eq!(
7818 view.selections.display_ranges(cx),
7819 &[DisplayPoint::new(2, 8)..DisplayPoint::new(2, 8)]
7820 );
7821
7822 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7823 assert_eq!(
7824 view.selections.display_ranges(cx),
7825 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7826 );
7827
7828 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7829 assert_eq!(
7830 view.selections.display_ranges(cx),
7831 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
7832 );
7833 });
7834 }
7835
7836 #[gpui::test]
7837 fn test_delete_to_beginning_of_line(cx: &mut gpui::MutableAppContext) {
7838 cx.set_global(Settings::test(cx));
7839 let (text, ranges) = marked_text_ranges("one [two three] four");
7840 let buffer = MultiBuffer::build_simple(&text, cx);
7841
7842 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7843
7844 editor.update(cx, |editor, cx| {
7845 editor.change_selections(None, cx, |s| s.select_ranges(ranges));
7846 editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
7847 assert_eq!(editor.text(cx), " four");
7848 });
7849 }
7850
7851 #[gpui::test]
7852 fn test_delete_to_word_boundary(cx: &mut gpui::MutableAppContext) {
7853 cx.set_global(Settings::test(cx));
7854 let buffer = MultiBuffer::build_simple("one two three four", cx);
7855 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7856
7857 view.update(cx, |view, cx| {
7858 view.change_selections(None, cx, |s| {
7859 s.select_display_ranges([
7860 // an empty selection - the preceding word fragment is deleted
7861 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7862 // characters selected - they are deleted
7863 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 12),
7864 ])
7865 });
7866 view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
7867 });
7868
7869 assert_eq!(buffer.read(cx).read(cx).text(), "e two te four");
7870
7871 view.update(cx, |view, cx| {
7872 view.change_selections(None, cx, |s| {
7873 s.select_display_ranges([
7874 // an empty selection - the following word fragment is deleted
7875 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7876 // characters selected - they are deleted
7877 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 10),
7878 ])
7879 });
7880 view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
7881 });
7882
7883 assert_eq!(buffer.read(cx).read(cx).text(), "e t te our");
7884 }
7885
7886 #[gpui::test]
7887 fn test_newline(cx: &mut gpui::MutableAppContext) {
7888 cx.set_global(Settings::test(cx));
7889 let buffer = MultiBuffer::build_simple("aaaa\n bbbb\n", cx);
7890 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7891
7892 view.update(cx, |view, cx| {
7893 view.change_selections(None, cx, |s| {
7894 s.select_display_ranges([
7895 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7896 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7897 DisplayPoint::new(1, 6)..DisplayPoint::new(1, 6),
7898 ])
7899 });
7900
7901 view.newline(&Newline, cx);
7902 assert_eq!(view.text(cx), "aa\naa\n \n bb\n bb\n");
7903 });
7904 }
7905
7906 #[gpui::test]
7907 fn test_newline_with_old_selections(cx: &mut gpui::MutableAppContext) {
7908 cx.set_global(Settings::test(cx));
7909 let buffer = MultiBuffer::build_simple(
7910 "
7911 a
7912 b(
7913 X
7914 )
7915 c(
7916 X
7917 )
7918 "
7919 .unindent()
7920 .as_str(),
7921 cx,
7922 );
7923
7924 let (_, editor) = cx.add_window(Default::default(), |cx| {
7925 let mut editor = build_editor(buffer.clone(), cx);
7926 editor.change_selections(None, cx, |s| {
7927 s.select_ranges([
7928 Point::new(2, 4)..Point::new(2, 5),
7929 Point::new(5, 4)..Point::new(5, 5),
7930 ])
7931 });
7932 editor
7933 });
7934
7935 // Edit the buffer directly, deleting ranges surrounding the editor's selections
7936 buffer.update(cx, |buffer, cx| {
7937 buffer.edit(
7938 [
7939 (Point::new(1, 2)..Point::new(3, 0), ""),
7940 (Point::new(4, 2)..Point::new(6, 0), ""),
7941 ],
7942 None,
7943 cx,
7944 );
7945 assert_eq!(
7946 buffer.read(cx).text(),
7947 "
7948 a
7949 b()
7950 c()
7951 "
7952 .unindent()
7953 );
7954 });
7955
7956 editor.update(cx, |editor, cx| {
7957 assert_eq!(
7958 editor.selections.ranges(cx),
7959 &[
7960 Point::new(1, 2)..Point::new(1, 2),
7961 Point::new(2, 2)..Point::new(2, 2),
7962 ],
7963 );
7964
7965 editor.newline(&Newline, cx);
7966 assert_eq!(
7967 editor.text(cx),
7968 "
7969 a
7970 b(
7971 )
7972 c(
7973 )
7974 "
7975 .unindent()
7976 );
7977
7978 // The selections are moved after the inserted newlines
7979 assert_eq!(
7980 editor.selections.ranges(cx),
7981 &[
7982 Point::new(2, 0)..Point::new(2, 0),
7983 Point::new(4, 0)..Point::new(4, 0),
7984 ],
7985 );
7986 });
7987 }
7988
7989 #[gpui::test]
7990 fn test_insert_with_old_selections(cx: &mut gpui::MutableAppContext) {
7991 cx.set_global(Settings::test(cx));
7992 let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
7993 let (_, editor) = cx.add_window(Default::default(), |cx| {
7994 let mut editor = build_editor(buffer.clone(), cx);
7995 editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
7996 editor
7997 });
7998
7999 // Edit the buffer directly, deleting ranges surrounding the editor's selections
8000 buffer.update(cx, |buffer, cx| {
8001 buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
8002 assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
8003 });
8004
8005 editor.update(cx, |editor, cx| {
8006 assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
8007
8008 editor.insert("Z", cx);
8009 assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
8010
8011 // The selections are moved after the inserted characters
8012 assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
8013 });
8014 }
8015
8016 #[gpui::test]
8017 async fn test_tab(cx: &mut gpui::TestAppContext) {
8018 let mut cx = EditorTestContext::new(cx).await;
8019 cx.update(|cx| {
8020 cx.update_global::<Settings, _, _>(|settings, _| {
8021 settings.editor_overrides.tab_size = Some(NonZeroU32::new(3).unwrap());
8022 });
8023 });
8024 cx.set_state(indoc! {"
8025 |ab|c
8026 |🏀|🏀|efg
8027 d|
8028 "});
8029 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8030 cx.assert_editor_state(indoc! {"
8031 |ab |c
8032 |🏀 |🏀 |efg
8033 d |
8034 "});
8035
8036 cx.set_state(indoc! {"
8037 a
8038 [🏀}🏀[🏀}🏀[🏀}
8039 "});
8040 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8041 cx.assert_editor_state(indoc! {"
8042 a
8043 [🏀}🏀[🏀}🏀[🏀}
8044 "});
8045 }
8046
8047 #[gpui::test]
8048 async fn test_tab_on_blank_line_auto_indents(cx: &mut gpui::TestAppContext) {
8049 let mut cx = EditorTestContext::new(cx).await;
8050 let language = Arc::new(
8051 Language::new(
8052 LanguageConfig::default(),
8053 Some(tree_sitter_rust::language()),
8054 )
8055 .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
8056 .unwrap(),
8057 );
8058 cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
8059
8060 // cursors that are already at the suggested indent level insert
8061 // a soft tab. cursors that are to the left of the suggested indent
8062 // auto-indent their line.
8063 cx.set_state(indoc! {"
8064 |
8065 const a: B = (
8066 c(
8067 d(
8068 |
8069 )
8070 |
8071 | )
8072 );
8073 "});
8074 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8075 cx.assert_editor_state(indoc! {"
8076 |
8077 const a: B = (
8078 c(
8079 d(
8080 |
8081 )
8082 |
8083 |)
8084 );
8085 "});
8086
8087 // handle auto-indent when there are multiple cursors on the same line
8088 cx.set_state(indoc! {"
8089 const a: B = (
8090 c(
8091 | |
8092 | )
8093 );
8094 "});
8095 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8096 cx.assert_editor_state(indoc! {"
8097 const a: B = (
8098 c(
8099 |
8100 |)
8101 );
8102 "});
8103 }
8104
8105 #[gpui::test]
8106 async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
8107 let mut cx = EditorTestContext::new(cx).await;
8108
8109 cx.set_state(indoc! {"
8110 [one} [two}
8111 three
8112 four"});
8113 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8114 cx.assert_editor_state(indoc! {"
8115 [one} [two}
8116 three
8117 four"});
8118
8119 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8120 cx.assert_editor_state(indoc! {"
8121 [one} [two}
8122 three
8123 four"});
8124
8125 // select across line ending
8126 cx.set_state(indoc! {"
8127 one two
8128 t[hree
8129 } four"});
8130 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8131 cx.assert_editor_state(indoc! {"
8132 one two
8133 t[hree
8134 } four"});
8135
8136 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8137 cx.assert_editor_state(indoc! {"
8138 one two
8139 t[hree
8140 } four"});
8141
8142 // Ensure that indenting/outdenting works when the cursor is at column 0.
8143 cx.set_state(indoc! {"
8144 one two
8145 |three
8146 four"});
8147 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8148 cx.assert_editor_state(indoc! {"
8149 one two
8150 |three
8151 four"});
8152
8153 cx.set_state(indoc! {"
8154 one two
8155 | three
8156 four"});
8157 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8158 cx.assert_editor_state(indoc! {"
8159 one two
8160 |three
8161 four"});
8162 }
8163
8164 #[gpui::test]
8165 async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
8166 let mut cx = EditorTestContext::new(cx).await;
8167 cx.update(|cx| {
8168 cx.update_global::<Settings, _, _>(|settings, _| {
8169 settings.editor_overrides.hard_tabs = Some(true);
8170 });
8171 });
8172
8173 // select two ranges on one line
8174 cx.set_state(indoc! {"
8175 [one} [two}
8176 three
8177 four"});
8178 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8179 cx.assert_editor_state(indoc! {"
8180 \t[one} [two}
8181 three
8182 four"});
8183 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8184 cx.assert_editor_state(indoc! {"
8185 \t\t[one} [two}
8186 three
8187 four"});
8188 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8189 cx.assert_editor_state(indoc! {"
8190 \t[one} [two}
8191 three
8192 four"});
8193 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8194 cx.assert_editor_state(indoc! {"
8195 [one} [two}
8196 three
8197 four"});
8198
8199 // select across a line ending
8200 cx.set_state(indoc! {"
8201 one two
8202 t[hree
8203 }four"});
8204 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8205 cx.assert_editor_state(indoc! {"
8206 one two
8207 \tt[hree
8208 }four"});
8209 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8210 cx.assert_editor_state(indoc! {"
8211 one two
8212 \t\tt[hree
8213 }four"});
8214 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8215 cx.assert_editor_state(indoc! {"
8216 one two
8217 \tt[hree
8218 }four"});
8219 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8220 cx.assert_editor_state(indoc! {"
8221 one two
8222 t[hree
8223 }four"});
8224
8225 // Ensure that indenting/outdenting works when the cursor is at column 0.
8226 cx.set_state(indoc! {"
8227 one two
8228 |three
8229 four"});
8230 cx.assert_editor_state(indoc! {"
8231 one two
8232 |three
8233 four"});
8234 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8235 cx.assert_editor_state(indoc! {"
8236 one two
8237 \t|three
8238 four"});
8239 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8240 cx.assert_editor_state(indoc! {"
8241 one two
8242 |three
8243 four"});
8244 }
8245
8246 #[gpui::test]
8247 fn test_indent_outdent_with_excerpts(cx: &mut gpui::MutableAppContext) {
8248 cx.set_global(
8249 Settings::test(cx)
8250 .with_language_defaults(
8251 "TOML",
8252 EditorSettings {
8253 tab_size: Some(2.try_into().unwrap()),
8254 ..Default::default()
8255 },
8256 )
8257 .with_language_defaults(
8258 "Rust",
8259 EditorSettings {
8260 tab_size: Some(4.try_into().unwrap()),
8261 ..Default::default()
8262 },
8263 ),
8264 );
8265 let toml_language = Arc::new(Language::new(
8266 LanguageConfig {
8267 name: "TOML".into(),
8268 ..Default::default()
8269 },
8270 None,
8271 ));
8272 let rust_language = Arc::new(Language::new(
8273 LanguageConfig {
8274 name: "Rust".into(),
8275 ..Default::default()
8276 },
8277 None,
8278 ));
8279
8280 let toml_buffer = cx
8281 .add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx).with_language(toml_language, cx));
8282 let rust_buffer = cx.add_model(|cx| {
8283 Buffer::new(0, "const c: usize = 3;\n", cx).with_language(rust_language, cx)
8284 });
8285 let multibuffer = cx.add_model(|cx| {
8286 let mut multibuffer = MultiBuffer::new(0);
8287 multibuffer.push_excerpts(
8288 toml_buffer.clone(),
8289 [ExcerptRange {
8290 context: Point::new(0, 0)..Point::new(2, 0),
8291 primary: None,
8292 }],
8293 cx,
8294 );
8295 multibuffer.push_excerpts(
8296 rust_buffer.clone(),
8297 [ExcerptRange {
8298 context: Point::new(0, 0)..Point::new(1, 0),
8299 primary: None,
8300 }],
8301 cx,
8302 );
8303 multibuffer
8304 });
8305
8306 cx.add_window(Default::default(), |cx| {
8307 let mut editor = build_editor(multibuffer, cx);
8308
8309 assert_eq!(
8310 editor.text(cx),
8311 indoc! {"
8312 a = 1
8313 b = 2
8314
8315 const c: usize = 3;
8316 "}
8317 );
8318
8319 select_ranges(
8320 &mut editor,
8321 indoc! {"
8322 [a] = 1
8323 b = 2
8324
8325 [const c:] usize = 3;
8326 "},
8327 cx,
8328 );
8329
8330 editor.tab(&Tab, cx);
8331 assert_text_with_selections(
8332 &mut editor,
8333 indoc! {"
8334 [a] = 1
8335 b = 2
8336
8337 [const c:] usize = 3;
8338 "},
8339 cx,
8340 );
8341 editor.tab_prev(&TabPrev, cx);
8342 assert_text_with_selections(
8343 &mut editor,
8344 indoc! {"
8345 [a] = 1
8346 b = 2
8347
8348 [const c:] usize = 3;
8349 "},
8350 cx,
8351 );
8352
8353 editor
8354 });
8355 }
8356
8357 #[gpui::test]
8358 async fn test_backspace(cx: &mut gpui::TestAppContext) {
8359 let mut cx = EditorTestContext::new(cx).await;
8360 // Basic backspace
8361 cx.set_state(indoc! {"
8362 on|e two three
8363 fou[r} five six
8364 seven {eight nine
8365 ]ten"});
8366 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
8367 cx.assert_editor_state(indoc! {"
8368 o|e two three
8369 fou| five six
8370 seven |ten"});
8371
8372 // Test backspace inside and around indents
8373 cx.set_state(indoc! {"
8374 zero
8375 |one
8376 |two
8377 | | | three
8378 | | four"});
8379 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
8380 cx.assert_editor_state(indoc! {"
8381 zero
8382 |one
8383 |two
8384 | three| four"});
8385
8386 // Test backspace with line_mode set to true
8387 cx.update_editor(|e, _| e.selections.line_mode = true);
8388 cx.set_state(indoc! {"
8389 The |quick |brown
8390 fox jumps over
8391 the lazy dog
8392 |The qu[ick b}rown"});
8393 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
8394 cx.assert_editor_state(indoc! {"
8395 |fox jumps over
8396 the lazy dog|"});
8397 }
8398
8399 #[gpui::test]
8400 async fn test_delete(cx: &mut gpui::TestAppContext) {
8401 let mut cx = EditorTestContext::new(cx).await;
8402
8403 cx.set_state(indoc! {"
8404 on|e two three
8405 fou[r} five six
8406 seven {eight nine
8407 ]ten"});
8408 cx.update_editor(|e, cx| e.delete(&Delete, cx));
8409 cx.assert_editor_state(indoc! {"
8410 on| two three
8411 fou| five six
8412 seven |ten"});
8413
8414 // Test backspace with line_mode set to true
8415 cx.update_editor(|e, _| e.selections.line_mode = true);
8416 cx.set_state(indoc! {"
8417 The |quick |brown
8418 fox {jum]ps over
8419 the lazy dog
8420 |The qu[ick b}rown"});
8421 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
8422 cx.assert_editor_state("|the lazy dog|");
8423 }
8424
8425 #[gpui::test]
8426 fn test_delete_line(cx: &mut gpui::MutableAppContext) {
8427 cx.set_global(Settings::test(cx));
8428 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8429 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8430 view.update(cx, |view, cx| {
8431 view.change_selections(None, cx, |s| {
8432 s.select_display_ranges([
8433 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8434 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
8435 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8436 ])
8437 });
8438 view.delete_line(&DeleteLine, cx);
8439 assert_eq!(view.display_text(cx), "ghi");
8440 assert_eq!(
8441 view.selections.display_ranges(cx),
8442 vec![
8443 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
8444 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
8445 ]
8446 );
8447 });
8448
8449 cx.set_global(Settings::test(cx));
8450 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8451 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8452 view.update(cx, |view, cx| {
8453 view.change_selections(None, cx, |s| {
8454 s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)])
8455 });
8456 view.delete_line(&DeleteLine, cx);
8457 assert_eq!(view.display_text(cx), "ghi\n");
8458 assert_eq!(
8459 view.selections.display_ranges(cx),
8460 vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
8461 );
8462 });
8463 }
8464
8465 #[gpui::test]
8466 fn test_duplicate_line(cx: &mut gpui::MutableAppContext) {
8467 cx.set_global(Settings::test(cx));
8468 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8469 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8470 view.update(cx, |view, cx| {
8471 view.change_selections(None, cx, |s| {
8472 s.select_display_ranges([
8473 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8474 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8475 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8476 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8477 ])
8478 });
8479 view.duplicate_line(&DuplicateLine, cx);
8480 assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
8481 assert_eq!(
8482 view.selections.display_ranges(cx),
8483 vec![
8484 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
8485 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
8486 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8487 DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
8488 ]
8489 );
8490 });
8491
8492 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8493 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8494 view.update(cx, |view, cx| {
8495 view.change_selections(None, cx, |s| {
8496 s.select_display_ranges([
8497 DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
8498 DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
8499 ])
8500 });
8501 view.duplicate_line(&DuplicateLine, cx);
8502 assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
8503 assert_eq!(
8504 view.selections.display_ranges(cx),
8505 vec![
8506 DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
8507 DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
8508 ]
8509 );
8510 });
8511 }
8512
8513 #[gpui::test]
8514 fn test_move_line_up_down(cx: &mut gpui::MutableAppContext) {
8515 cx.set_global(Settings::test(cx));
8516 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
8517 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8518 view.update(cx, |view, cx| {
8519 view.fold_ranges(
8520 vec![
8521 Point::new(0, 2)..Point::new(1, 2),
8522 Point::new(2, 3)..Point::new(4, 1),
8523 Point::new(7, 0)..Point::new(8, 4),
8524 ],
8525 cx,
8526 );
8527 view.change_selections(None, cx, |s| {
8528 s.select_display_ranges([
8529 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8530 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8531 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8532 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
8533 ])
8534 });
8535 assert_eq!(
8536 view.display_text(cx),
8537 "aa…bbb\nccc…eeee\nfffff\nggggg\n…i\njjjjj"
8538 );
8539
8540 view.move_line_up(&MoveLineUp, cx);
8541 assert_eq!(
8542 view.display_text(cx),
8543 "aa…bbb\nccc…eeee\nggggg\n…i\njjjjj\nfffff"
8544 );
8545 assert_eq!(
8546 view.selections.display_ranges(cx),
8547 vec![
8548 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8549 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8550 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
8551 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
8552 ]
8553 );
8554 });
8555
8556 view.update(cx, |view, cx| {
8557 view.move_line_down(&MoveLineDown, cx);
8558 assert_eq!(
8559 view.display_text(cx),
8560 "ccc…eeee\naa…bbb\nfffff\nggggg\n…i\njjjjj"
8561 );
8562 assert_eq!(
8563 view.selections.display_ranges(cx),
8564 vec![
8565 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8566 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8567 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8568 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
8569 ]
8570 );
8571 });
8572
8573 view.update(cx, |view, cx| {
8574 view.move_line_down(&MoveLineDown, cx);
8575 assert_eq!(
8576 view.display_text(cx),
8577 "ccc…eeee\nfffff\naa…bbb\nggggg\n…i\njjjjj"
8578 );
8579 assert_eq!(
8580 view.selections.display_ranges(cx),
8581 vec![
8582 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8583 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8584 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8585 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
8586 ]
8587 );
8588 });
8589
8590 view.update(cx, |view, cx| {
8591 view.move_line_up(&MoveLineUp, cx);
8592 assert_eq!(
8593 view.display_text(cx),
8594 "ccc…eeee\naa…bbb\nggggg\n…i\njjjjj\nfffff"
8595 );
8596 assert_eq!(
8597 view.selections.display_ranges(cx),
8598 vec![
8599 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8600 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8601 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
8602 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
8603 ]
8604 );
8605 });
8606 }
8607
8608 #[gpui::test]
8609 fn test_move_line_up_down_with_blocks(cx: &mut gpui::MutableAppContext) {
8610 cx.set_global(Settings::test(cx));
8611 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
8612 let snapshot = buffer.read(cx).snapshot(cx);
8613 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8614 editor.update(cx, |editor, cx| {
8615 editor.insert_blocks(
8616 [BlockProperties {
8617 style: BlockStyle::Fixed,
8618 position: snapshot.anchor_after(Point::new(2, 0)),
8619 disposition: BlockDisposition::Below,
8620 height: 1,
8621 render: Arc::new(|_| Empty::new().boxed()),
8622 }],
8623 cx,
8624 );
8625 editor.change_selections(None, cx, |s| {
8626 s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
8627 });
8628 editor.move_line_down(&MoveLineDown, cx);
8629 });
8630 }
8631
8632 #[gpui::test]
8633 fn test_transpose(cx: &mut gpui::MutableAppContext) {
8634 cx.set_global(Settings::test(cx));
8635
8636 cx.add_window(Default::default(), |cx| {
8637 let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
8638
8639 editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
8640 editor.transpose(&Default::default(), cx);
8641 assert_eq!(editor.text(cx), "bac");
8642 assert_eq!(editor.selections.ranges(cx), [2..2]);
8643
8644 editor.transpose(&Default::default(), cx);
8645 assert_eq!(editor.text(cx), "bca");
8646 assert_eq!(editor.selections.ranges(cx), [3..3]);
8647
8648 editor.transpose(&Default::default(), cx);
8649 assert_eq!(editor.text(cx), "bac");
8650 assert_eq!(editor.selections.ranges(cx), [3..3]);
8651
8652 editor
8653 })
8654 .1;
8655
8656 cx.add_window(Default::default(), |cx| {
8657 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
8658
8659 editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
8660 editor.transpose(&Default::default(), cx);
8661 assert_eq!(editor.text(cx), "acb\nde");
8662 assert_eq!(editor.selections.ranges(cx), [3..3]);
8663
8664 editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
8665 editor.transpose(&Default::default(), cx);
8666 assert_eq!(editor.text(cx), "acbd\ne");
8667 assert_eq!(editor.selections.ranges(cx), [5..5]);
8668
8669 editor.transpose(&Default::default(), cx);
8670 assert_eq!(editor.text(cx), "acbde\n");
8671 assert_eq!(editor.selections.ranges(cx), [6..6]);
8672
8673 editor.transpose(&Default::default(), cx);
8674 assert_eq!(editor.text(cx), "acbd\ne");
8675 assert_eq!(editor.selections.ranges(cx), [6..6]);
8676
8677 editor
8678 })
8679 .1;
8680
8681 cx.add_window(Default::default(), |cx| {
8682 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
8683
8684 editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
8685 editor.transpose(&Default::default(), cx);
8686 assert_eq!(editor.text(cx), "bacd\ne");
8687 assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
8688
8689 editor.transpose(&Default::default(), cx);
8690 assert_eq!(editor.text(cx), "bcade\n");
8691 assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
8692
8693 editor.transpose(&Default::default(), cx);
8694 assert_eq!(editor.text(cx), "bcda\ne");
8695 assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
8696
8697 editor.transpose(&Default::default(), cx);
8698 assert_eq!(editor.text(cx), "bcade\n");
8699 assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
8700
8701 editor.transpose(&Default::default(), cx);
8702 assert_eq!(editor.text(cx), "bcaed\n");
8703 assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
8704
8705 editor
8706 })
8707 .1;
8708
8709 cx.add_window(Default::default(), |cx| {
8710 let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
8711
8712 editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
8713 editor.transpose(&Default::default(), cx);
8714 assert_eq!(editor.text(cx), "🏀🍐✋");
8715 assert_eq!(editor.selections.ranges(cx), [8..8]);
8716
8717 editor.transpose(&Default::default(), cx);
8718 assert_eq!(editor.text(cx), "🏀✋🍐");
8719 assert_eq!(editor.selections.ranges(cx), [11..11]);
8720
8721 editor.transpose(&Default::default(), cx);
8722 assert_eq!(editor.text(cx), "🏀🍐✋");
8723 assert_eq!(editor.selections.ranges(cx), [11..11]);
8724
8725 editor
8726 })
8727 .1;
8728 }
8729
8730 #[gpui::test]
8731 async fn test_clipboard(cx: &mut gpui::TestAppContext) {
8732 let mut cx = EditorTestContext::new(cx).await;
8733
8734 cx.set_state("[one✅ }two [three }four [five }six ");
8735 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8736 cx.assert_editor_state("|two |four |six ");
8737
8738 // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
8739 cx.set_state("two |four |six |");
8740 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8741 cx.assert_editor_state("two one✅ |four three |six five |");
8742
8743 // Paste again but with only two cursors. Since the number of cursors doesn't
8744 // match the number of slices in the clipboard, the entire clipboard text
8745 // is pasted at each cursor.
8746 cx.set_state("|two one✅ four three six five |");
8747 cx.update_editor(|e, cx| {
8748 e.handle_input("( ", cx);
8749 e.paste(&Paste, cx);
8750 e.handle_input(") ", cx);
8751 });
8752 cx.assert_editor_state(indoc! {"
8753 ( one✅
8754 three
8755 five ) |two one✅ four three six five ( one✅
8756 three
8757 five ) |"});
8758
8759 // Cut with three selections, one of which is full-line.
8760 cx.set_state(indoc! {"
8761 1[2}3
8762 4|567
8763 [8}9"});
8764 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8765 cx.assert_editor_state(indoc! {"
8766 1|3
8767 |9"});
8768
8769 // Paste with three selections, noticing how the copied selection that was full-line
8770 // gets inserted before the second cursor.
8771 cx.set_state(indoc! {"
8772 1|3
8773 9|
8774 [o}ne"});
8775 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8776 cx.assert_editor_state(indoc! {"
8777 12|3
8778 4567
8779 9|
8780 8|ne"});
8781
8782 // Copy with a single cursor only, which writes the whole line into the clipboard.
8783 cx.set_state(indoc! {"
8784 The quick brown
8785 fox ju|mps over
8786 the lazy dog"});
8787 cx.update_editor(|e, cx| e.copy(&Copy, cx));
8788 cx.cx.assert_clipboard_content(Some("fox jumps over\n"));
8789
8790 // Paste with three selections, noticing how the copied full-line selection is inserted
8791 // before the empty selections but replaces the selection that is non-empty.
8792 cx.set_state(indoc! {"
8793 T|he quick brown
8794 [fo}x jumps over
8795 t|he lazy dog"});
8796 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8797 cx.assert_editor_state(indoc! {"
8798 fox jumps over
8799 T|he quick brown
8800 fox jumps over
8801 |x jumps over
8802 fox jumps over
8803 t|he lazy dog"});
8804 }
8805
8806 #[gpui::test]
8807 async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
8808 let mut cx = EditorTestContext::new(cx).await;
8809 let language = Arc::new(Language::new(
8810 LanguageConfig::default(),
8811 Some(tree_sitter_rust::language()),
8812 ));
8813 cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
8814
8815 // Cut an indented block, without the leading whitespace.
8816 cx.set_state(indoc! {"
8817 const a: B = (
8818 c(),
8819 [d(
8820 e,
8821 f
8822 )}
8823 );
8824 "});
8825 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8826 cx.assert_editor_state(indoc! {"
8827 const a: B = (
8828 c(),
8829 |
8830 );
8831 "});
8832
8833 // Paste it at the same position.
8834 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8835 cx.assert_editor_state(indoc! {"
8836 const a: B = (
8837 c(),
8838 d(
8839 e,
8840 f
8841 )|
8842 );
8843 "});
8844
8845 // Paste it at a line with a lower indent level.
8846 cx.set_state(indoc! {"
8847 |
8848 const a: B = (
8849 c(),
8850 );
8851 "});
8852 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8853 cx.assert_editor_state(indoc! {"
8854 d(
8855 e,
8856 f
8857 )|
8858 const a: B = (
8859 c(),
8860 );
8861 "});
8862
8863 // Cut an indented block, with the leading whitespace.
8864 cx.set_state(indoc! {"
8865 const a: B = (
8866 c(),
8867 [ d(
8868 e,
8869 f
8870 )
8871 });
8872 "});
8873 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8874 cx.assert_editor_state(indoc! {"
8875 const a: B = (
8876 c(),
8877 |);
8878 "});
8879
8880 // Paste it at the same position.
8881 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8882 cx.assert_editor_state(indoc! {"
8883 const a: B = (
8884 c(),
8885 d(
8886 e,
8887 f
8888 )
8889 |);
8890 "});
8891
8892 // Paste it at a line with a higher indent level.
8893 cx.set_state(indoc! {"
8894 const a: B = (
8895 c(),
8896 d(
8897 e,
8898 f|
8899 )
8900 );
8901 "});
8902 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8903 cx.assert_editor_state(indoc! {"
8904 const a: B = (
8905 c(),
8906 d(
8907 e,
8908 f d(
8909 e,
8910 f
8911 )
8912 |
8913 )
8914 );
8915 "});
8916 }
8917
8918 #[gpui::test]
8919 fn test_select_all(cx: &mut gpui::MutableAppContext) {
8920 cx.set_global(Settings::test(cx));
8921 let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
8922 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8923 view.update(cx, |view, cx| {
8924 view.select_all(&SelectAll, cx);
8925 assert_eq!(
8926 view.selections.display_ranges(cx),
8927 &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
8928 );
8929 });
8930 }
8931
8932 #[gpui::test]
8933 fn test_select_line(cx: &mut gpui::MutableAppContext) {
8934 cx.set_global(Settings::test(cx));
8935 let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
8936 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8937 view.update(cx, |view, cx| {
8938 view.change_selections(None, cx, |s| {
8939 s.select_display_ranges([
8940 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8941 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8942 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8943 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
8944 ])
8945 });
8946 view.select_line(&SelectLine, cx);
8947 assert_eq!(
8948 view.selections.display_ranges(cx),
8949 vec![
8950 DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
8951 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
8952 ]
8953 );
8954 });
8955
8956 view.update(cx, |view, cx| {
8957 view.select_line(&SelectLine, cx);
8958 assert_eq!(
8959 view.selections.display_ranges(cx),
8960 vec![
8961 DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
8962 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
8963 ]
8964 );
8965 });
8966
8967 view.update(cx, |view, cx| {
8968 view.select_line(&SelectLine, cx);
8969 assert_eq!(
8970 view.selections.display_ranges(cx),
8971 vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
8972 );
8973 });
8974 }
8975
8976 #[gpui::test]
8977 fn test_split_selection_into_lines(cx: &mut gpui::MutableAppContext) {
8978 cx.set_global(Settings::test(cx));
8979 let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
8980 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8981 view.update(cx, |view, cx| {
8982 view.fold_ranges(
8983 vec![
8984 Point::new(0, 2)..Point::new(1, 2),
8985 Point::new(2, 3)..Point::new(4, 1),
8986 Point::new(7, 0)..Point::new(8, 4),
8987 ],
8988 cx,
8989 );
8990 view.change_selections(None, cx, |s| {
8991 s.select_display_ranges([
8992 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8993 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8994 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8995 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
8996 ])
8997 });
8998 assert_eq!(view.display_text(cx), "aa…bbb\nccc…eeee\nfffff\nggggg\n…i");
8999 });
9000
9001 view.update(cx, |view, cx| {
9002 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
9003 assert_eq!(
9004 view.display_text(cx),
9005 "aaaaa\nbbbbb\nccc…eeee\nfffff\nggggg\n…i"
9006 );
9007 assert_eq!(
9008 view.selections.display_ranges(cx),
9009 [
9010 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
9011 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
9012 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
9013 DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
9014 ]
9015 );
9016 });
9017
9018 view.update(cx, |view, cx| {
9019 view.change_selections(None, cx, |s| {
9020 s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
9021 });
9022 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
9023 assert_eq!(
9024 view.display_text(cx),
9025 "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
9026 );
9027 assert_eq!(
9028 view.selections.display_ranges(cx),
9029 [
9030 DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
9031 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
9032 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
9033 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
9034 DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
9035 DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
9036 DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
9037 DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
9038 ]
9039 );
9040 });
9041 }
9042
9043 #[gpui::test]
9044 fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) {
9045 cx.set_global(Settings::test(cx));
9046 let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
9047 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
9048
9049 view.update(cx, |view, cx| {
9050 view.change_selections(None, cx, |s| {
9051 s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)])
9052 });
9053 });
9054 view.update(cx, |view, cx| {
9055 view.add_selection_above(&AddSelectionAbove, cx);
9056 assert_eq!(
9057 view.selections.display_ranges(cx),
9058 vec![
9059 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
9060 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
9061 ]
9062 );
9063 });
9064
9065 view.update(cx, |view, cx| {
9066 view.add_selection_above(&AddSelectionAbove, cx);
9067 assert_eq!(
9068 view.selections.display_ranges(cx),
9069 vec![
9070 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
9071 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
9072 ]
9073 );
9074 });
9075
9076 view.update(cx, |view, cx| {
9077 view.add_selection_below(&AddSelectionBelow, cx);
9078 assert_eq!(
9079 view.selections.display_ranges(cx),
9080 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
9081 );
9082
9083 view.undo_selection(&UndoSelection, cx);
9084 assert_eq!(
9085 view.selections.display_ranges(cx),
9086 vec![
9087 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
9088 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
9089 ]
9090 );
9091
9092 view.redo_selection(&RedoSelection, cx);
9093 assert_eq!(
9094 view.selections.display_ranges(cx),
9095 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
9096 );
9097 });
9098
9099 view.update(cx, |view, cx| {
9100 view.add_selection_below(&AddSelectionBelow, cx);
9101 assert_eq!(
9102 view.selections.display_ranges(cx),
9103 vec![
9104 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
9105 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
9106 ]
9107 );
9108 });
9109
9110 view.update(cx, |view, cx| {
9111 view.add_selection_below(&AddSelectionBelow, cx);
9112 assert_eq!(
9113 view.selections.display_ranges(cx),
9114 vec![
9115 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
9116 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
9117 ]
9118 );
9119 });
9120
9121 view.update(cx, |view, cx| {
9122 view.change_selections(None, cx, |s| {
9123 s.select_display_ranges([DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)])
9124 });
9125 });
9126 view.update(cx, |view, cx| {
9127 view.add_selection_below(&AddSelectionBelow, cx);
9128 assert_eq!(
9129 view.selections.display_ranges(cx),
9130 vec![
9131 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
9132 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
9133 ]
9134 );
9135 });
9136
9137 view.update(cx, |view, cx| {
9138 view.add_selection_below(&AddSelectionBelow, cx);
9139 assert_eq!(
9140 view.selections.display_ranges(cx),
9141 vec![
9142 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
9143 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
9144 ]
9145 );
9146 });
9147
9148 view.update(cx, |view, cx| {
9149 view.add_selection_above(&AddSelectionAbove, cx);
9150 assert_eq!(
9151 view.selections.display_ranges(cx),
9152 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
9153 );
9154 });
9155
9156 view.update(cx, |view, cx| {
9157 view.add_selection_above(&AddSelectionAbove, cx);
9158 assert_eq!(
9159 view.selections.display_ranges(cx),
9160 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
9161 );
9162 });
9163
9164 view.update(cx, |view, cx| {
9165 view.change_selections(None, cx, |s| {
9166 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)])
9167 });
9168 view.add_selection_below(&AddSelectionBelow, cx);
9169 assert_eq!(
9170 view.selections.display_ranges(cx),
9171 vec![
9172 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
9173 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
9174 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
9175 ]
9176 );
9177 });
9178
9179 view.update(cx, |view, cx| {
9180 view.add_selection_below(&AddSelectionBelow, cx);
9181 assert_eq!(
9182 view.selections.display_ranges(cx),
9183 vec![
9184 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
9185 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
9186 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
9187 DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
9188 ]
9189 );
9190 });
9191
9192 view.update(cx, |view, cx| {
9193 view.add_selection_above(&AddSelectionAbove, cx);
9194 assert_eq!(
9195 view.selections.display_ranges(cx),
9196 vec![
9197 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
9198 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
9199 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
9200 ]
9201 );
9202 });
9203
9204 view.update(cx, |view, cx| {
9205 view.change_selections(None, cx, |s| {
9206 s.select_display_ranges([DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)])
9207 });
9208 });
9209 view.update(cx, |view, cx| {
9210 view.add_selection_above(&AddSelectionAbove, cx);
9211 assert_eq!(
9212 view.selections.display_ranges(cx),
9213 vec![
9214 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
9215 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
9216 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
9217 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
9218 ]
9219 );
9220 });
9221
9222 view.update(cx, |view, cx| {
9223 view.add_selection_below(&AddSelectionBelow, cx);
9224 assert_eq!(
9225 view.selections.display_ranges(cx),
9226 vec![
9227 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
9228 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
9229 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
9230 ]
9231 );
9232 });
9233 }
9234
9235 #[gpui::test]
9236 fn test_select_next(cx: &mut gpui::MutableAppContext) {
9237 cx.set_global(Settings::test(cx));
9238
9239 let (text, ranges) = marked_text_ranges("[abc]\n[abc] [abc]\ndefabc\n[abc]");
9240 let buffer = MultiBuffer::build_simple(&text, cx);
9241 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
9242
9243 view.update(cx, |view, cx| {
9244 view.change_selections(None, cx, |s| {
9245 s.select_ranges([ranges[1].start + 1..ranges[1].start + 1])
9246 });
9247 view.select_next(
9248 &SelectNext {
9249 replace_newest: false,
9250 },
9251 cx,
9252 );
9253 assert_eq!(view.selections.ranges(cx), &ranges[1..2]);
9254
9255 view.select_next(
9256 &SelectNext {
9257 replace_newest: false,
9258 },
9259 cx,
9260 );
9261 assert_eq!(view.selections.ranges(cx), &ranges[1..3]);
9262
9263 view.undo_selection(&UndoSelection, cx);
9264 assert_eq!(view.selections.ranges(cx), &ranges[1..2]);
9265
9266 view.redo_selection(&RedoSelection, cx);
9267 assert_eq!(view.selections.ranges(cx), &ranges[1..3]);
9268
9269 view.select_next(
9270 &SelectNext {
9271 replace_newest: false,
9272 },
9273 cx,
9274 );
9275 assert_eq!(view.selections.ranges(cx), &ranges[1..4]);
9276
9277 view.select_next(
9278 &SelectNext {
9279 replace_newest: false,
9280 },
9281 cx,
9282 );
9283 assert_eq!(view.selections.ranges(cx), &ranges[0..4]);
9284 });
9285 }
9286
9287 #[gpui::test]
9288 async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
9289 cx.update(|cx| cx.set_global(Settings::test(cx)));
9290 let language = Arc::new(Language::new(
9291 LanguageConfig::default(),
9292 Some(tree_sitter_rust::language()),
9293 ));
9294
9295 let text = r#"
9296 use mod1::mod2::{mod3, mod4};
9297
9298 fn fn_1(param1: bool, param2: &str) {
9299 let var1 = "text";
9300 }
9301 "#
9302 .unindent();
9303
9304 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9305 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9306 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9307 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9308 .await;
9309
9310 view.update(cx, |view, cx| {
9311 view.change_selections(None, cx, |s| {
9312 s.select_display_ranges([
9313 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
9314 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
9315 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
9316 ]);
9317 });
9318 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9319 });
9320 assert_eq!(
9321 view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
9322 &[
9323 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
9324 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
9325 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
9326 ]
9327 );
9328
9329 view.update(cx, |view, cx| {
9330 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9331 });
9332 assert_eq!(
9333 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9334 &[
9335 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
9336 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
9337 ]
9338 );
9339
9340 view.update(cx, |view, cx| {
9341 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9342 });
9343 assert_eq!(
9344 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9345 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
9346 );
9347
9348 // Trying to expand the selected syntax node one more time has no effect.
9349 view.update(cx, |view, cx| {
9350 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9351 });
9352 assert_eq!(
9353 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9354 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
9355 );
9356
9357 view.update(cx, |view, cx| {
9358 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
9359 });
9360 assert_eq!(
9361 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9362 &[
9363 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
9364 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
9365 ]
9366 );
9367
9368 view.update(cx, |view, cx| {
9369 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
9370 });
9371 assert_eq!(
9372 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9373 &[
9374 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
9375 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
9376 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
9377 ]
9378 );
9379
9380 view.update(cx, |view, cx| {
9381 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
9382 });
9383 assert_eq!(
9384 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9385 &[
9386 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
9387 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
9388 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
9389 ]
9390 );
9391
9392 // Trying to shrink the selected syntax node one more time has no effect.
9393 view.update(cx, |view, cx| {
9394 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
9395 });
9396 assert_eq!(
9397 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9398 &[
9399 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
9400 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
9401 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
9402 ]
9403 );
9404
9405 // Ensure that we keep expanding the selection if the larger selection starts or ends within
9406 // a fold.
9407 view.update(cx, |view, cx| {
9408 view.fold_ranges(
9409 vec![
9410 Point::new(0, 21)..Point::new(0, 24),
9411 Point::new(3, 20)..Point::new(3, 22),
9412 ],
9413 cx,
9414 );
9415 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9416 });
9417 assert_eq!(
9418 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9419 &[
9420 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
9421 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
9422 DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
9423 ]
9424 );
9425 }
9426
9427 #[gpui::test]
9428 async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
9429 cx.update(|cx| cx.set_global(Settings::test(cx)));
9430 let language = Arc::new(
9431 Language::new(
9432 LanguageConfig {
9433 brackets: vec![
9434 BracketPair {
9435 start: "{".to_string(),
9436 end: "}".to_string(),
9437 close: false,
9438 newline: true,
9439 },
9440 BracketPair {
9441 start: "(".to_string(),
9442 end: ")".to_string(),
9443 close: false,
9444 newline: true,
9445 },
9446 ],
9447 ..Default::default()
9448 },
9449 Some(tree_sitter_rust::language()),
9450 )
9451 .with_indents_query(
9452 r#"
9453 (_ "(" ")" @end) @indent
9454 (_ "{" "}" @end) @indent
9455 "#,
9456 )
9457 .unwrap(),
9458 );
9459
9460 let text = "fn a() {}";
9461
9462 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9463 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9464 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9465 editor
9466 .condition(&cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
9467 .await;
9468
9469 editor.update(cx, |editor, cx| {
9470 editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
9471 editor.newline(&Newline, cx);
9472 assert_eq!(editor.text(cx), "fn a(\n \n) {\n \n}\n");
9473 assert_eq!(
9474 editor.selections.ranges(cx),
9475 &[
9476 Point::new(1, 4)..Point::new(1, 4),
9477 Point::new(3, 4)..Point::new(3, 4),
9478 Point::new(5, 0)..Point::new(5, 0)
9479 ]
9480 );
9481 });
9482 }
9483
9484 #[gpui::test]
9485 async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
9486 cx.update(|cx| cx.set_global(Settings::test(cx)));
9487 let language = Arc::new(Language::new(
9488 LanguageConfig {
9489 brackets: vec![
9490 BracketPair {
9491 start: "{".to_string(),
9492 end: "}".to_string(),
9493 close: true,
9494 newline: true,
9495 },
9496 BracketPair {
9497 start: "/*".to_string(),
9498 end: " */".to_string(),
9499 close: true,
9500 newline: true,
9501 },
9502 BracketPair {
9503 start: "[".to_string(),
9504 end: "]".to_string(),
9505 close: false,
9506 newline: true,
9507 },
9508 ],
9509 autoclose_before: "})]".to_string(),
9510 ..Default::default()
9511 },
9512 Some(tree_sitter_rust::language()),
9513 ));
9514
9515 let text = r#"
9516 a
9517
9518 /
9519
9520 "#
9521 .unindent();
9522
9523 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9524 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9525 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9526 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9527 .await;
9528
9529 view.update(cx, |view, cx| {
9530 view.change_selections(None, cx, |s| {
9531 s.select_display_ranges([
9532 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9533 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
9534 ])
9535 });
9536
9537 view.handle_input("{", cx);
9538 view.handle_input("{", cx);
9539 view.handle_input("{", cx);
9540 assert_eq!(
9541 view.text(cx),
9542 "
9543 {{{}}}
9544 {{{}}}
9545 /
9546
9547 "
9548 .unindent()
9549 );
9550
9551 view.move_right(&MoveRight, cx);
9552 view.handle_input("}", cx);
9553 view.handle_input("}", cx);
9554 view.handle_input("}", cx);
9555 assert_eq!(
9556 view.text(cx),
9557 "
9558 {{{}}}}
9559 {{{}}}}
9560 /
9561
9562 "
9563 .unindent()
9564 );
9565
9566 view.undo(&Undo, cx);
9567 view.handle_input("/", cx);
9568 view.handle_input("*", cx);
9569 assert_eq!(
9570 view.text(cx),
9571 "
9572 /* */
9573 /* */
9574 /
9575
9576 "
9577 .unindent()
9578 );
9579
9580 view.undo(&Undo, cx);
9581 view.change_selections(None, cx, |s| {
9582 s.select_display_ranges([
9583 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
9584 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
9585 ])
9586 });
9587 view.handle_input("*", cx);
9588 assert_eq!(
9589 view.text(cx),
9590 "
9591 a
9592
9593 /*
9594 *
9595 "
9596 .unindent()
9597 );
9598
9599 // Don't autoclose if the next character isn't whitespace and isn't
9600 // listed in the language's "autoclose_before" section.
9601 view.finalize_last_transaction(cx);
9602 view.change_selections(None, cx, |s| {
9603 s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)])
9604 });
9605 view.handle_input("{", cx);
9606 assert_eq!(
9607 view.text(cx),
9608 "
9609 {a
9610
9611 /*
9612 *
9613 "
9614 .unindent()
9615 );
9616
9617 view.undo(&Undo, cx);
9618 view.change_selections(None, cx, |s| {
9619 s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1)])
9620 });
9621 view.handle_input("{", cx);
9622 assert_eq!(
9623 view.text(cx),
9624 "
9625 {a}
9626
9627 /*
9628 *
9629 "
9630 .unindent()
9631 );
9632 assert_eq!(
9633 view.selections.display_ranges(cx),
9634 [DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]
9635 );
9636
9637 view.undo(&Undo, cx);
9638 view.handle_input("[", cx);
9639 assert_eq!(
9640 view.text(cx),
9641 "
9642 [a]
9643
9644 /*
9645 *
9646 "
9647 .unindent()
9648 );
9649 assert_eq!(
9650 view.selections.display_ranges(cx),
9651 [DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]
9652 );
9653
9654 view.undo(&Undo, cx);
9655 view.change_selections(None, cx, |s| {
9656 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)])
9657 });
9658 view.handle_input("[", cx);
9659 assert_eq!(
9660 view.text(cx),
9661 "
9662 a[
9663
9664 /*
9665 *
9666 "
9667 .unindent()
9668 );
9669 assert_eq!(
9670 view.selections.display_ranges(cx),
9671 [DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2)]
9672 );
9673 });
9674 }
9675
9676 #[gpui::test]
9677 async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
9678 cx.update(|cx| cx.set_global(Settings::test(cx)));
9679 let language = Arc::new(Language::new(
9680 LanguageConfig {
9681 brackets: vec![BracketPair {
9682 start: "{".to_string(),
9683 end: "}".to_string(),
9684 close: true,
9685 newline: true,
9686 }],
9687 ..Default::default()
9688 },
9689 Some(tree_sitter_rust::language()),
9690 ));
9691
9692 let text = r#"
9693 a
9694 b
9695 c
9696 "#
9697 .unindent();
9698
9699 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9700 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9701 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9702 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9703 .await;
9704
9705 view.update(cx, |view, cx| {
9706 view.change_selections(None, cx, |s| {
9707 s.select_display_ranges([
9708 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9709 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
9710 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
9711 ])
9712 });
9713
9714 view.handle_input("{", cx);
9715 view.handle_input("{", cx);
9716 view.handle_input("{", cx);
9717 assert_eq!(
9718 view.text(cx),
9719 "
9720 {{{a}}}
9721 {{{b}}}
9722 {{{c}}}
9723 "
9724 .unindent()
9725 );
9726 assert_eq!(
9727 view.selections.display_ranges(cx),
9728 [
9729 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
9730 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
9731 DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
9732 ]
9733 );
9734
9735 view.undo(&Undo, cx);
9736 assert_eq!(
9737 view.text(cx),
9738 "
9739 a
9740 b
9741 c
9742 "
9743 .unindent()
9744 );
9745 assert_eq!(
9746 view.selections.display_ranges(cx),
9747 [
9748 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9749 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
9750 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
9751 ]
9752 );
9753 });
9754 }
9755
9756 #[gpui::test]
9757 async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
9758 cx.update(|cx| cx.set_global(Settings::test(cx)));
9759 let language = Arc::new(Language::new(
9760 LanguageConfig {
9761 brackets: vec![BracketPair {
9762 start: "{".to_string(),
9763 end: "}".to_string(),
9764 close: true,
9765 newline: true,
9766 }],
9767 autoclose_before: "}".to_string(),
9768 ..Default::default()
9769 },
9770 Some(tree_sitter_rust::language()),
9771 ));
9772
9773 let text = r#"
9774 a
9775 b
9776 c
9777 "#
9778 .unindent();
9779
9780 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9781 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9782 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9783 editor
9784 .condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9785 .await;
9786
9787 editor.update(cx, |editor, cx| {
9788 editor.change_selections(None, cx, |s| {
9789 s.select_ranges([
9790 Point::new(0, 1)..Point::new(0, 1),
9791 Point::new(1, 1)..Point::new(1, 1),
9792 Point::new(2, 1)..Point::new(2, 1),
9793 ])
9794 });
9795
9796 editor.handle_input("{", cx);
9797 editor.handle_input("{", cx);
9798 editor.handle_input("_", cx);
9799 assert_eq!(
9800 editor.text(cx),
9801 "
9802 a{{_}}
9803 b{{_}}
9804 c{{_}}
9805 "
9806 .unindent()
9807 );
9808 assert_eq!(
9809 editor.selections.ranges::<Point>(cx),
9810 [
9811 Point::new(0, 4)..Point::new(0, 4),
9812 Point::new(1, 4)..Point::new(1, 4),
9813 Point::new(2, 4)..Point::new(2, 4)
9814 ]
9815 );
9816
9817 editor.backspace(&Default::default(), cx);
9818 editor.backspace(&Default::default(), cx);
9819 assert_eq!(
9820 editor.text(cx),
9821 "
9822 a{}
9823 b{}
9824 c{}
9825 "
9826 .unindent()
9827 );
9828 assert_eq!(
9829 editor.selections.ranges::<Point>(cx),
9830 [
9831 Point::new(0, 2)..Point::new(0, 2),
9832 Point::new(1, 2)..Point::new(1, 2),
9833 Point::new(2, 2)..Point::new(2, 2)
9834 ]
9835 );
9836
9837 editor.delete_to_previous_word_start(&Default::default(), cx);
9838 assert_eq!(
9839 editor.text(cx),
9840 "
9841 a
9842 b
9843 c
9844 "
9845 .unindent()
9846 );
9847 assert_eq!(
9848 editor.selections.ranges::<Point>(cx),
9849 [
9850 Point::new(0, 1)..Point::new(0, 1),
9851 Point::new(1, 1)..Point::new(1, 1),
9852 Point::new(2, 1)..Point::new(2, 1)
9853 ]
9854 );
9855 });
9856 }
9857
9858 #[gpui::test]
9859 async fn test_snippets(cx: &mut gpui::TestAppContext) {
9860 cx.update(|cx| cx.set_global(Settings::test(cx)));
9861
9862 let (text, insertion_ranges) = marked_text_ranges(indoc! {"
9863 a.| b
9864 a.| b
9865 a.| b"});
9866 let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
9867 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9868
9869 editor.update(cx, |editor, cx| {
9870 let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
9871
9872 editor
9873 .insert_snippet(&insertion_ranges, snippet, cx)
9874 .unwrap();
9875
9876 fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text_ranges: &str) {
9877 let range_markers = ('<', '>');
9878 let (expected_text, mut selection_ranges_lookup) =
9879 marked_text_ranges_by(marked_text_ranges, vec![range_markers.clone().into()]);
9880 let selection_ranges = selection_ranges_lookup
9881 .remove(&range_markers.into())
9882 .unwrap();
9883 assert_eq!(editor.text(cx), expected_text);
9884 assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
9885 }
9886 assert(
9887 editor,
9888 cx,
9889 indoc! {"
9890 a.f(<one>, two, <three>) b
9891 a.f(<one>, two, <three>) b
9892 a.f(<one>, two, <three>) b"},
9893 );
9894
9895 // Can't move earlier than the first tab stop
9896 assert!(!editor.move_to_prev_snippet_tabstop(cx));
9897 assert(
9898 editor,
9899 cx,
9900 indoc! {"
9901 a.f(<one>, two, <three>) b
9902 a.f(<one>, two, <three>) b
9903 a.f(<one>, two, <three>) b"},
9904 );
9905
9906 assert!(editor.move_to_next_snippet_tabstop(cx));
9907 assert(
9908 editor,
9909 cx,
9910 indoc! {"
9911 a.f(one, <two>, three) b
9912 a.f(one, <two>, three) b
9913 a.f(one, <two>, three) b"},
9914 );
9915
9916 editor.move_to_prev_snippet_tabstop(cx);
9917 assert(
9918 editor,
9919 cx,
9920 indoc! {"
9921 a.f(<one>, two, <three>) b
9922 a.f(<one>, two, <three>) b
9923 a.f(<one>, two, <three>) b"},
9924 );
9925
9926 assert!(editor.move_to_next_snippet_tabstop(cx));
9927 assert(
9928 editor,
9929 cx,
9930 indoc! {"
9931 a.f(one, <two>, three) b
9932 a.f(one, <two>, three) b
9933 a.f(one, <two>, three) b"},
9934 );
9935 assert!(editor.move_to_next_snippet_tabstop(cx));
9936 assert(
9937 editor,
9938 cx,
9939 indoc! {"
9940 a.f(one, two, three)<> b
9941 a.f(one, two, three)<> b
9942 a.f(one, two, three)<> b"},
9943 );
9944
9945 // As soon as the last tab stop is reached, snippet state is gone
9946 editor.move_to_prev_snippet_tabstop(cx);
9947 assert(
9948 editor,
9949 cx,
9950 indoc! {"
9951 a.f(one, two, three)<> b
9952 a.f(one, two, three)<> b
9953 a.f(one, two, three)<> b"},
9954 );
9955 });
9956 }
9957
9958 #[gpui::test]
9959 async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
9960 cx.foreground().forbid_parking();
9961
9962 let mut language = Language::new(
9963 LanguageConfig {
9964 name: "Rust".into(),
9965 path_suffixes: vec!["rs".to_string()],
9966 ..Default::default()
9967 },
9968 Some(tree_sitter_rust::language()),
9969 );
9970 let mut fake_servers = language
9971 .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
9972 capabilities: lsp::ServerCapabilities {
9973 document_formatting_provider: Some(lsp::OneOf::Left(true)),
9974 ..Default::default()
9975 },
9976 ..Default::default()
9977 }))
9978 .await;
9979
9980 let fs = FakeFs::new(cx.background().clone());
9981 fs.insert_file("/file.rs", Default::default()).await;
9982
9983 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
9984 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
9985 let buffer = project
9986 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
9987 .await
9988 .unwrap();
9989
9990 cx.foreground().start_waiting();
9991 let fake_server = fake_servers.next().await.unwrap();
9992
9993 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9994 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9995 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9996 assert!(cx.read(|cx| editor.is_dirty(cx)));
9997
9998 let save = cx.update(|cx| editor.save(project.clone(), cx));
9999 fake_server
10000 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
10001 assert_eq!(
10002 params.text_document.uri,
10003 lsp::Url::from_file_path("/file.rs").unwrap()
10004 );
10005 assert_eq!(params.options.tab_size, 4);
10006 Ok(Some(vec![lsp::TextEdit::new(
10007 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
10008 ", ".to_string(),
10009 )]))
10010 })
10011 .next()
10012 .await;
10013 cx.foreground().start_waiting();
10014 save.await.unwrap();
10015 assert_eq!(
10016 editor.read_with(cx, |editor, cx| editor.text(cx)),
10017 "one, two\nthree\n"
10018 );
10019 assert!(!cx.read(|cx| editor.is_dirty(cx)));
10020
10021 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
10022 assert!(cx.read(|cx| editor.is_dirty(cx)));
10023
10024 // Ensure we can still save even if formatting hangs.
10025 fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
10026 assert_eq!(
10027 params.text_document.uri,
10028 lsp::Url::from_file_path("/file.rs").unwrap()
10029 );
10030 futures::future::pending::<()>().await;
10031 unreachable!()
10032 });
10033 let save = cx.update(|cx| editor.save(project.clone(), cx));
10034 cx.foreground().advance_clock(items::FORMAT_TIMEOUT);
10035 cx.foreground().start_waiting();
10036 save.await.unwrap();
10037 assert_eq!(
10038 editor.read_with(cx, |editor, cx| editor.text(cx)),
10039 "one\ntwo\nthree\n"
10040 );
10041 assert!(!cx.read(|cx| editor.is_dirty(cx)));
10042
10043 // Set rust language override and assert overriden tabsize is sent to language server
10044 cx.update(|cx| {
10045 cx.update_global::<Settings, _, _>(|settings, _| {
10046 settings.language_overrides.insert(
10047 "Rust".into(),
10048 EditorSettings {
10049 tab_size: Some(8.try_into().unwrap()),
10050 ..Default::default()
10051 },
10052 );
10053 })
10054 });
10055
10056 let save = cx.update(|cx| editor.save(project.clone(), cx));
10057 fake_server
10058 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
10059 assert_eq!(
10060 params.text_document.uri,
10061 lsp::Url::from_file_path("/file.rs").unwrap()
10062 );
10063 assert_eq!(params.options.tab_size, 8);
10064 Ok(Some(vec![]))
10065 })
10066 .next()
10067 .await;
10068 cx.foreground().start_waiting();
10069 save.await.unwrap();
10070 }
10071
10072 #[gpui::test]
10073 async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
10074 cx.foreground().forbid_parking();
10075
10076 let mut language = Language::new(
10077 LanguageConfig {
10078 name: "Rust".into(),
10079 path_suffixes: vec!["rs".to_string()],
10080 ..Default::default()
10081 },
10082 Some(tree_sitter_rust::language()),
10083 );
10084 let mut fake_servers = language
10085 .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
10086 capabilities: lsp::ServerCapabilities {
10087 document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
10088 ..Default::default()
10089 },
10090 ..Default::default()
10091 }))
10092 .await;
10093
10094 let fs = FakeFs::new(cx.background().clone());
10095 fs.insert_file("/file.rs", Default::default()).await;
10096
10097 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10098 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
10099 let buffer = project
10100 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
10101 .await
10102 .unwrap();
10103
10104 cx.foreground().start_waiting();
10105 let fake_server = fake_servers.next().await.unwrap();
10106
10107 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10108 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
10109 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
10110 assert!(cx.read(|cx| editor.is_dirty(cx)));
10111
10112 let save = cx.update(|cx| editor.save(project.clone(), cx));
10113 fake_server
10114 .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
10115 assert_eq!(
10116 params.text_document.uri,
10117 lsp::Url::from_file_path("/file.rs").unwrap()
10118 );
10119 assert_eq!(params.options.tab_size, 4);
10120 Ok(Some(vec![lsp::TextEdit::new(
10121 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
10122 ", ".to_string(),
10123 )]))
10124 })
10125 .next()
10126 .await;
10127 cx.foreground().start_waiting();
10128 save.await.unwrap();
10129 assert_eq!(
10130 editor.read_with(cx, |editor, cx| editor.text(cx)),
10131 "one, two\nthree\n"
10132 );
10133 assert!(!cx.read(|cx| editor.is_dirty(cx)));
10134
10135 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
10136 assert!(cx.read(|cx| editor.is_dirty(cx)));
10137
10138 // Ensure we can still save even if formatting hangs.
10139 fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
10140 move |params, _| async move {
10141 assert_eq!(
10142 params.text_document.uri,
10143 lsp::Url::from_file_path("/file.rs").unwrap()
10144 );
10145 futures::future::pending::<()>().await;
10146 unreachable!()
10147 },
10148 );
10149 let save = cx.update(|cx| editor.save(project.clone(), cx));
10150 cx.foreground().advance_clock(items::FORMAT_TIMEOUT);
10151 cx.foreground().start_waiting();
10152 save.await.unwrap();
10153 assert_eq!(
10154 editor.read_with(cx, |editor, cx| editor.text(cx)),
10155 "one\ntwo\nthree\n"
10156 );
10157 assert!(!cx.read(|cx| editor.is_dirty(cx)));
10158
10159 // Set rust language override and assert overriden tabsize is sent to language server
10160 cx.update(|cx| {
10161 cx.update_global::<Settings, _, _>(|settings, _| {
10162 settings.language_overrides.insert(
10163 "Rust".into(),
10164 EditorSettings {
10165 tab_size: Some(8.try_into().unwrap()),
10166 ..Default::default()
10167 },
10168 );
10169 })
10170 });
10171
10172 let save = cx.update(|cx| editor.save(project.clone(), cx));
10173 fake_server
10174 .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
10175 assert_eq!(
10176 params.text_document.uri,
10177 lsp::Url::from_file_path("/file.rs").unwrap()
10178 );
10179 assert_eq!(params.options.tab_size, 8);
10180 Ok(Some(vec![]))
10181 })
10182 .next()
10183 .await;
10184 cx.foreground().start_waiting();
10185 save.await.unwrap();
10186 }
10187
10188 #[gpui::test]
10189 async fn test_completion(cx: &mut gpui::TestAppContext) {
10190 let mut cx = EditorLspTestContext::new_rust(
10191 lsp::ServerCapabilities {
10192 completion_provider: Some(lsp::CompletionOptions {
10193 trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10194 ..Default::default()
10195 }),
10196 ..Default::default()
10197 },
10198 cx,
10199 )
10200 .await;
10201
10202 cx.set_state(indoc! {"
10203 one|
10204 two
10205 three"});
10206 cx.simulate_keystroke(".");
10207 handle_completion_request(
10208 &mut cx,
10209 indoc! {"
10210 one.|<>
10211 two
10212 three"},
10213 vec!["first_completion", "second_completion"],
10214 )
10215 .await;
10216 cx.condition(|editor, _| editor.context_menu_visible())
10217 .await;
10218 let apply_additional_edits = cx.update_editor(|editor, cx| {
10219 editor.move_down(&MoveDown, cx);
10220 editor
10221 .confirm_completion(&ConfirmCompletion::default(), cx)
10222 .unwrap()
10223 });
10224 cx.assert_editor_state(indoc! {"
10225 one.second_completion|
10226 two
10227 three"});
10228
10229 handle_resolve_completion_request(
10230 &mut cx,
10231 Some((
10232 indoc! {"
10233 one.second_completion
10234 two
10235 three<>"},
10236 "\nadditional edit",
10237 )),
10238 )
10239 .await;
10240 apply_additional_edits.await.unwrap();
10241 cx.assert_editor_state(indoc! {"
10242 one.second_completion|
10243 two
10244 three
10245 additional edit"});
10246
10247 cx.set_state(indoc! {"
10248 one.second_completion
10249 two|
10250 three|
10251 additional edit"});
10252 cx.simulate_keystroke(" ");
10253 assert!(cx.editor(|e, _| e.context_menu.is_none()));
10254 cx.simulate_keystroke("s");
10255 assert!(cx.editor(|e, _| e.context_menu.is_none()));
10256
10257 cx.assert_editor_state(indoc! {"
10258 one.second_completion
10259 two s|
10260 three s|
10261 additional edit"});
10262 handle_completion_request(
10263 &mut cx,
10264 indoc! {"
10265 one.second_completion
10266 two s
10267 three <s|>
10268 additional edit"},
10269 vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10270 )
10271 .await;
10272 cx.condition(|editor, _| editor.context_menu_visible())
10273 .await;
10274
10275 cx.simulate_keystroke("i");
10276
10277 handle_completion_request(
10278 &mut cx,
10279 indoc! {"
10280 one.second_completion
10281 two si
10282 three <si|>
10283 additional edit"},
10284 vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10285 )
10286 .await;
10287 cx.condition(|editor, _| editor.context_menu_visible())
10288 .await;
10289
10290 let apply_additional_edits = cx.update_editor(|editor, cx| {
10291 editor
10292 .confirm_completion(&ConfirmCompletion::default(), cx)
10293 .unwrap()
10294 });
10295 cx.assert_editor_state(indoc! {"
10296 one.second_completion
10297 two sixth_completion|
10298 three sixth_completion|
10299 additional edit"});
10300
10301 handle_resolve_completion_request(&mut cx, None).await;
10302 apply_additional_edits.await.unwrap();
10303
10304 cx.update(|cx| {
10305 cx.update_global::<Settings, _, _>(|settings, _| {
10306 settings.show_completions_on_input = false;
10307 })
10308 });
10309 cx.set_state("editor|");
10310 cx.simulate_keystroke(".");
10311 assert!(cx.editor(|e, _| e.context_menu.is_none()));
10312 cx.simulate_keystroke("c");
10313 cx.simulate_keystroke("l");
10314 cx.simulate_keystroke("o");
10315 cx.assert_editor_state("editor.clo|");
10316 assert!(cx.editor(|e, _| e.context_menu.is_none()));
10317 cx.update_editor(|editor, cx| {
10318 editor.show_completions(&ShowCompletions, cx);
10319 });
10320 handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
10321 cx.condition(|editor, _| editor.context_menu_visible())
10322 .await;
10323 let apply_additional_edits = cx.update_editor(|editor, cx| {
10324 editor
10325 .confirm_completion(&ConfirmCompletion::default(), cx)
10326 .unwrap()
10327 });
10328 cx.assert_editor_state("editor.close|");
10329 handle_resolve_completion_request(&mut cx, None).await;
10330 apply_additional_edits.await.unwrap();
10331
10332 // Handle completion request passing a marked string specifying where the completion
10333 // should be triggered from using '|' character, what range should be replaced, and what completions
10334 // should be returned using '<' and '>' to delimit the range
10335 async fn handle_completion_request<'a>(
10336 cx: &mut EditorLspTestContext<'a>,
10337 marked_string: &str,
10338 completions: Vec<&'static str>,
10339 ) {
10340 let complete_from_marker: TextRangeMarker = '|'.into();
10341 let replace_range_marker: TextRangeMarker = ('<', '>').into();
10342 let (_, mut marked_ranges) = marked_text_ranges_by(
10343 marked_string,
10344 vec![complete_from_marker.clone(), replace_range_marker.clone()],
10345 );
10346
10347 let complete_from_position =
10348 cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
10349 let replace_range =
10350 cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
10351
10352 cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
10353 let completions = completions.clone();
10354 async move {
10355 assert_eq!(params.text_document_position.text_document.uri, url.clone());
10356 assert_eq!(
10357 params.text_document_position.position,
10358 complete_from_position
10359 );
10360 Ok(Some(lsp::CompletionResponse::Array(
10361 completions
10362 .iter()
10363 .map(|completion_text| lsp::CompletionItem {
10364 label: completion_text.to_string(),
10365 text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10366 range: replace_range.clone(),
10367 new_text: completion_text.to_string(),
10368 })),
10369 ..Default::default()
10370 })
10371 .collect(),
10372 )))
10373 }
10374 })
10375 .next()
10376 .await;
10377 }
10378
10379 async fn handle_resolve_completion_request<'a>(
10380 cx: &mut EditorLspTestContext<'a>,
10381 edit: Option<(&'static str, &'static str)>,
10382 ) {
10383 let edit = edit.map(|(marked_string, new_text)| {
10384 let replace_range_marker: TextRangeMarker = ('<', '>').into();
10385 let (_, mut marked_ranges) =
10386 marked_text_ranges_by(marked_string, vec![replace_range_marker.clone()]);
10387
10388 let replace_range = cx
10389 .to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
10390
10391 vec![lsp::TextEdit::new(replace_range, new_text.to_string())]
10392 });
10393
10394 cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
10395 let edit = edit.clone();
10396 async move {
10397 Ok(lsp::CompletionItem {
10398 additional_text_edits: edit,
10399 ..Default::default()
10400 })
10401 }
10402 })
10403 .next()
10404 .await;
10405 }
10406 }
10407
10408 #[gpui::test]
10409 async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
10410 cx.update(|cx| cx.set_global(Settings::test(cx)));
10411 let language = Arc::new(Language::new(
10412 LanguageConfig {
10413 line_comment: Some("// ".to_string()),
10414 ..Default::default()
10415 },
10416 Some(tree_sitter_rust::language()),
10417 ));
10418
10419 let text = "
10420 fn a() {
10421 //b();
10422 // c();
10423 // d();
10424 }
10425 "
10426 .unindent();
10427
10428 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
10429 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10430 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
10431
10432 view.update(cx, |editor, cx| {
10433 // If multiple selections intersect a line, the line is only
10434 // toggled once.
10435 editor.change_selections(None, cx, |s| {
10436 s.select_display_ranges([
10437 DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3),
10438 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6),
10439 ])
10440 });
10441 editor.toggle_comments(&ToggleComments, cx);
10442 assert_eq!(
10443 editor.text(cx),
10444 "
10445 fn a() {
10446 b();
10447 c();
10448 d();
10449 }
10450 "
10451 .unindent()
10452 );
10453
10454 // The comment prefix is inserted at the same column for every line
10455 // in a selection.
10456 editor.change_selections(None, cx, |s| {
10457 s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)])
10458 });
10459 editor.toggle_comments(&ToggleComments, cx);
10460 assert_eq!(
10461 editor.text(cx),
10462 "
10463 fn a() {
10464 // b();
10465 // c();
10466 // d();
10467 }
10468 "
10469 .unindent()
10470 );
10471
10472 // If a selection ends at the beginning of a line, that line is not toggled.
10473 editor.change_selections(None, cx, |s| {
10474 s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)])
10475 });
10476 editor.toggle_comments(&ToggleComments, cx);
10477 assert_eq!(
10478 editor.text(cx),
10479 "
10480 fn a() {
10481 // b();
10482 c();
10483 // d();
10484 }
10485 "
10486 .unindent()
10487 );
10488 });
10489 }
10490
10491 #[gpui::test]
10492 fn test_editing_disjoint_excerpts(cx: &mut gpui::MutableAppContext) {
10493 cx.set_global(Settings::test(cx));
10494 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
10495 let multibuffer = cx.add_model(|cx| {
10496 let mut multibuffer = MultiBuffer::new(0);
10497 multibuffer.push_excerpts(
10498 buffer.clone(),
10499 [
10500 ExcerptRange {
10501 context: Point::new(0, 0)..Point::new(0, 4),
10502 primary: None,
10503 },
10504 ExcerptRange {
10505 context: Point::new(1, 0)..Point::new(1, 4),
10506 primary: None,
10507 },
10508 ],
10509 cx,
10510 );
10511 multibuffer
10512 });
10513
10514 assert_eq!(multibuffer.read(cx).read(cx).text(), "aaaa\nbbbb");
10515
10516 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
10517 view.update(cx, |view, cx| {
10518 assert_eq!(view.text(cx), "aaaa\nbbbb");
10519 view.change_selections(None, cx, |s| {
10520 s.select_ranges([
10521 Point::new(0, 0)..Point::new(0, 0),
10522 Point::new(1, 0)..Point::new(1, 0),
10523 ])
10524 });
10525
10526 view.handle_input("X", cx);
10527 assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
10528 assert_eq!(
10529 view.selections.ranges(cx),
10530 [
10531 Point::new(0, 1)..Point::new(0, 1),
10532 Point::new(1, 1)..Point::new(1, 1),
10533 ]
10534 )
10535 });
10536 }
10537
10538 #[gpui::test]
10539 fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) {
10540 cx.set_global(Settings::test(cx));
10541 let (initial_text, excerpt_ranges) = marked_text_ranges(indoc! {"
10542 [aaaa
10543 (bbbb]
10544 cccc)"});
10545 let excerpt_ranges = excerpt_ranges.into_iter().map(|context| ExcerptRange {
10546 context,
10547 primary: None,
10548 });
10549 let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx));
10550 let multibuffer = cx.add_model(|cx| {
10551 let mut multibuffer = MultiBuffer::new(0);
10552 multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
10553 multibuffer
10554 });
10555
10556 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
10557 view.update(cx, |view, cx| {
10558 let (expected_text, selection_ranges) = marked_text_ranges(indoc! {"
10559 aaaa
10560 b|bbb
10561 b|bb|b
10562 cccc"});
10563 assert_eq!(view.text(cx), expected_text);
10564 view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
10565
10566 view.handle_input("X", cx);
10567
10568 let (expected_text, expected_selections) = marked_text_ranges(indoc! {"
10569 aaaa
10570 bX|bbXb
10571 bX|bbX|b
10572 cccc"});
10573 assert_eq!(view.text(cx), expected_text);
10574 assert_eq!(view.selections.ranges(cx), expected_selections);
10575
10576 view.newline(&Newline, cx);
10577 let (expected_text, expected_selections) = marked_text_ranges(indoc! {"
10578 aaaa
10579 bX
10580 |bbX
10581 b
10582 bX
10583 |bbX
10584 |b
10585 cccc"});
10586 assert_eq!(view.text(cx), expected_text);
10587 assert_eq!(view.selections.ranges(cx), expected_selections);
10588 });
10589 }
10590
10591 #[gpui::test]
10592 fn test_refresh_selections(cx: &mut gpui::MutableAppContext) {
10593 cx.set_global(Settings::test(cx));
10594 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
10595 let mut excerpt1_id = None;
10596 let multibuffer = cx.add_model(|cx| {
10597 let mut multibuffer = MultiBuffer::new(0);
10598 excerpt1_id = multibuffer
10599 .push_excerpts(
10600 buffer.clone(),
10601 [
10602 ExcerptRange {
10603 context: Point::new(0, 0)..Point::new(1, 4),
10604 primary: None,
10605 },
10606 ExcerptRange {
10607 context: Point::new(1, 0)..Point::new(2, 4),
10608 primary: None,
10609 },
10610 ],
10611 cx,
10612 )
10613 .into_iter()
10614 .next();
10615 multibuffer
10616 });
10617 assert_eq!(
10618 multibuffer.read(cx).read(cx).text(),
10619 "aaaa\nbbbb\nbbbb\ncccc"
10620 );
10621 let (_, editor) = cx.add_window(Default::default(), |cx| {
10622 let mut editor = build_editor(multibuffer.clone(), cx);
10623 let snapshot = editor.snapshot(cx);
10624 editor.change_selections(None, cx, |s| {
10625 s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
10626 });
10627 editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
10628 assert_eq!(
10629 editor.selections.ranges(cx),
10630 [
10631 Point::new(1, 3)..Point::new(1, 3),
10632 Point::new(2, 1)..Point::new(2, 1),
10633 ]
10634 );
10635 editor
10636 });
10637
10638 // Refreshing selections is a no-op when excerpts haven't changed.
10639 editor.update(cx, |editor, cx| {
10640 editor.change_selections(None, cx, |s| {
10641 s.refresh();
10642 });
10643 assert_eq!(
10644 editor.selections.ranges(cx),
10645 [
10646 Point::new(1, 3)..Point::new(1, 3),
10647 Point::new(2, 1)..Point::new(2, 1),
10648 ]
10649 );
10650 });
10651
10652 multibuffer.update(cx, |multibuffer, cx| {
10653 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
10654 });
10655 editor.update(cx, |editor, cx| {
10656 // Removing an excerpt causes the first selection to become degenerate.
10657 assert_eq!(
10658 editor.selections.ranges(cx),
10659 [
10660 Point::new(0, 0)..Point::new(0, 0),
10661 Point::new(0, 1)..Point::new(0, 1)
10662 ]
10663 );
10664
10665 // Refreshing selections will relocate the first selection to the original buffer
10666 // location.
10667 editor.change_selections(None, cx, |s| {
10668 s.refresh();
10669 });
10670 assert_eq!(
10671 editor.selections.ranges(cx),
10672 [
10673 Point::new(0, 1)..Point::new(0, 1),
10674 Point::new(0, 3)..Point::new(0, 3)
10675 ]
10676 );
10677 assert!(editor.selections.pending_anchor().is_some());
10678 });
10679 }
10680
10681 #[gpui::test]
10682 fn test_refresh_selections_while_selecting_with_mouse(cx: &mut gpui::MutableAppContext) {
10683 cx.set_global(Settings::test(cx));
10684 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
10685 let mut excerpt1_id = None;
10686 let multibuffer = cx.add_model(|cx| {
10687 let mut multibuffer = MultiBuffer::new(0);
10688 excerpt1_id = multibuffer
10689 .push_excerpts(
10690 buffer.clone(),
10691 [
10692 ExcerptRange {
10693 context: Point::new(0, 0)..Point::new(1, 4),
10694 primary: None,
10695 },
10696 ExcerptRange {
10697 context: Point::new(1, 0)..Point::new(2, 4),
10698 primary: None,
10699 },
10700 ],
10701 cx,
10702 )
10703 .into_iter()
10704 .next();
10705 multibuffer
10706 });
10707 assert_eq!(
10708 multibuffer.read(cx).read(cx).text(),
10709 "aaaa\nbbbb\nbbbb\ncccc"
10710 );
10711 let (_, editor) = cx.add_window(Default::default(), |cx| {
10712 let mut editor = build_editor(multibuffer.clone(), cx);
10713 let snapshot = editor.snapshot(cx);
10714 editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
10715 assert_eq!(
10716 editor.selections.ranges(cx),
10717 [Point::new(1, 3)..Point::new(1, 3)]
10718 );
10719 editor
10720 });
10721
10722 multibuffer.update(cx, |multibuffer, cx| {
10723 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
10724 });
10725 editor.update(cx, |editor, cx| {
10726 assert_eq!(
10727 editor.selections.ranges(cx),
10728 [Point::new(0, 0)..Point::new(0, 0)]
10729 );
10730
10731 // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
10732 editor.change_selections(None, cx, |s| {
10733 s.refresh();
10734 });
10735 assert_eq!(
10736 editor.selections.ranges(cx),
10737 [Point::new(0, 3)..Point::new(0, 3)]
10738 );
10739 assert!(editor.selections.pending_anchor().is_some());
10740 });
10741 }
10742
10743 #[gpui::test]
10744 async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
10745 cx.update(|cx| cx.set_global(Settings::test(cx)));
10746 let language = Arc::new(
10747 Language::new(
10748 LanguageConfig {
10749 brackets: vec![
10750 BracketPair {
10751 start: "{".to_string(),
10752 end: "}".to_string(),
10753 close: true,
10754 newline: true,
10755 },
10756 BracketPair {
10757 start: "/* ".to_string(),
10758 end: " */".to_string(),
10759 close: true,
10760 newline: true,
10761 },
10762 ],
10763 ..Default::default()
10764 },
10765 Some(tree_sitter_rust::language()),
10766 )
10767 .with_indents_query("")
10768 .unwrap(),
10769 );
10770
10771 let text = concat!(
10772 "{ }\n", // Suppress rustfmt
10773 " x\n", //
10774 " /* */\n", //
10775 "x\n", //
10776 "{{} }\n", //
10777 );
10778
10779 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
10780 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10781 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
10782 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
10783 .await;
10784
10785 view.update(cx, |view, cx| {
10786 view.change_selections(None, cx, |s| {
10787 s.select_display_ranges([
10788 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
10789 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
10790 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
10791 ])
10792 });
10793 view.newline(&Newline, cx);
10794
10795 assert_eq!(
10796 view.buffer().read(cx).read(cx).text(),
10797 concat!(
10798 "{ \n", // Suppress rustfmt
10799 "\n", //
10800 "}\n", //
10801 " x\n", //
10802 " /* \n", //
10803 " \n", //
10804 " */\n", //
10805 "x\n", //
10806 "{{} \n", //
10807 "}\n", //
10808 )
10809 );
10810 });
10811 }
10812
10813 #[gpui::test]
10814 fn test_highlighted_ranges(cx: &mut gpui::MutableAppContext) {
10815 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10816
10817 cx.set_global(Settings::test(cx));
10818 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
10819
10820 editor.update(cx, |editor, cx| {
10821 struct Type1;
10822 struct Type2;
10823
10824 let buffer = buffer.read(cx).snapshot(cx);
10825
10826 let anchor_range = |range: Range<Point>| {
10827 buffer.anchor_after(range.start)..buffer.anchor_after(range.end)
10828 };
10829
10830 editor.highlight_background::<Type1>(
10831 vec![
10832 anchor_range(Point::new(2, 1)..Point::new(2, 3)),
10833 anchor_range(Point::new(4, 2)..Point::new(4, 4)),
10834 anchor_range(Point::new(6, 3)..Point::new(6, 5)),
10835 anchor_range(Point::new(8, 4)..Point::new(8, 6)),
10836 ],
10837 |_| Color::red(),
10838 cx,
10839 );
10840 editor.highlight_background::<Type2>(
10841 vec![
10842 anchor_range(Point::new(3, 2)..Point::new(3, 5)),
10843 anchor_range(Point::new(5, 3)..Point::new(5, 6)),
10844 anchor_range(Point::new(7, 4)..Point::new(7, 7)),
10845 anchor_range(Point::new(9, 5)..Point::new(9, 8)),
10846 ],
10847 |_| Color::green(),
10848 cx,
10849 );
10850
10851 let snapshot = editor.snapshot(cx);
10852 let mut highlighted_ranges = editor.background_highlights_in_range(
10853 anchor_range(Point::new(3, 4)..Point::new(7, 4)),
10854 &snapshot,
10855 cx.global::<Settings>().theme.as_ref(),
10856 );
10857 // Enforce a consistent ordering based on color without relying on the ordering of the
10858 // highlight's `TypeId` which is non-deterministic.
10859 highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
10860 assert_eq!(
10861 highlighted_ranges,
10862 &[
10863 (
10864 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
10865 Color::green(),
10866 ),
10867 (
10868 DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
10869 Color::green(),
10870 ),
10871 (
10872 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
10873 Color::red(),
10874 ),
10875 (
10876 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
10877 Color::red(),
10878 ),
10879 ]
10880 );
10881 assert_eq!(
10882 editor.background_highlights_in_range(
10883 anchor_range(Point::new(5, 6)..Point::new(6, 4)),
10884 &snapshot,
10885 cx.global::<Settings>().theme.as_ref(),
10886 ),
10887 &[(
10888 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
10889 Color::red(),
10890 )]
10891 );
10892 });
10893 }
10894
10895 #[gpui::test]
10896 fn test_following(cx: &mut gpui::MutableAppContext) {
10897 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10898
10899 cx.set_global(Settings::test(cx));
10900
10901 let (_, leader) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
10902 let (_, follower) = cx.add_window(
10903 WindowOptions {
10904 bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
10905 ..Default::default()
10906 },
10907 |cx| build_editor(buffer.clone(), cx),
10908 );
10909
10910 let pending_update = Rc::new(RefCell::new(None));
10911 follower.update(cx, {
10912 let update = pending_update.clone();
10913 |_, cx| {
10914 cx.subscribe(&leader, move |_, leader, event, cx| {
10915 leader
10916 .read(cx)
10917 .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
10918 })
10919 .detach();
10920 }
10921 });
10922
10923 // Update the selections only
10924 leader.update(cx, |leader, cx| {
10925 leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
10926 });
10927 follower.update(cx, |follower, cx| {
10928 follower
10929 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10930 .unwrap();
10931 });
10932 assert_eq!(follower.read(cx).selections.ranges(cx), vec![1..1]);
10933
10934 // Update the scroll position only
10935 leader.update(cx, |leader, cx| {
10936 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
10937 });
10938 follower.update(cx, |follower, cx| {
10939 follower
10940 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10941 .unwrap();
10942 });
10943 assert_eq!(
10944 follower.update(cx, |follower, cx| follower.scroll_position(cx)),
10945 vec2f(1.5, 3.5)
10946 );
10947
10948 // Update the selections and scroll position
10949 leader.update(cx, |leader, cx| {
10950 leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
10951 leader.request_autoscroll(Autoscroll::Newest, cx);
10952 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
10953 });
10954 follower.update(cx, |follower, cx| {
10955 let initial_scroll_position = follower.scroll_position(cx);
10956 follower
10957 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10958 .unwrap();
10959 assert_eq!(follower.scroll_position(cx), initial_scroll_position);
10960 assert!(follower.autoscroll_request.is_some());
10961 });
10962 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0]);
10963
10964 // Creating a pending selection that precedes another selection
10965 leader.update(cx, |leader, cx| {
10966 leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
10967 leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
10968 });
10969 follower.update(cx, |follower, cx| {
10970 follower
10971 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10972 .unwrap();
10973 });
10974 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0, 1..1]);
10975
10976 // Extend the pending selection so that it surrounds another selection
10977 leader.update(cx, |leader, cx| {
10978 leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
10979 });
10980 follower.update(cx, |follower, cx| {
10981 follower
10982 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10983 .unwrap();
10984 });
10985 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..2]);
10986 }
10987
10988 #[test]
10989 fn test_combine_syntax_and_fuzzy_match_highlights() {
10990 let string = "abcdefghijklmnop";
10991 let syntax_ranges = [
10992 (
10993 0..3,
10994 HighlightStyle {
10995 color: Some(Color::red()),
10996 ..Default::default()
10997 },
10998 ),
10999 (
11000 4..8,
11001 HighlightStyle {
11002 color: Some(Color::green()),
11003 ..Default::default()
11004 },
11005 ),
11006 ];
11007 let match_indices = [4, 6, 7, 8];
11008 assert_eq!(
11009 combine_syntax_and_fuzzy_match_highlights(
11010 &string,
11011 Default::default(),
11012 syntax_ranges.into_iter(),
11013 &match_indices,
11014 ),
11015 &[
11016 (
11017 0..3,
11018 HighlightStyle {
11019 color: Some(Color::red()),
11020 ..Default::default()
11021 },
11022 ),
11023 (
11024 4..5,
11025 HighlightStyle {
11026 color: Some(Color::green()),
11027 weight: Some(fonts::Weight::BOLD),
11028 ..Default::default()
11029 },
11030 ),
11031 (
11032 5..6,
11033 HighlightStyle {
11034 color: Some(Color::green()),
11035 ..Default::default()
11036 },
11037 ),
11038 (
11039 6..8,
11040 HighlightStyle {
11041 color: Some(Color::green()),
11042 weight: Some(fonts::Weight::BOLD),
11043 ..Default::default()
11044 },
11045 ),
11046 (
11047 8..9,
11048 HighlightStyle {
11049 weight: Some(fonts::Weight::BOLD),
11050 ..Default::default()
11051 },
11052 ),
11053 ]
11054 );
11055 }
11056
11057 fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
11058 let point = DisplayPoint::new(row as u32, column as u32);
11059 point..point
11060 }
11061
11062 fn assert_selection_ranges(
11063 marked_text: &str,
11064 selection_marker_pairs: Vec<(char, char)>,
11065 view: &mut Editor,
11066 cx: &mut ViewContext<Editor>,
11067 ) {
11068 let snapshot = view.snapshot(cx).display_snapshot;
11069 let mut marker_chars = Vec::new();
11070 for (start, end) in selection_marker_pairs.iter() {
11071 marker_chars.push(*start);
11072 marker_chars.push(*end);
11073 }
11074 let (_, markers) = marked_text_by(marked_text, marker_chars);
11075 let asserted_ranges: Vec<Range<DisplayPoint>> = selection_marker_pairs
11076 .iter()
11077 .map(|(start, end)| {
11078 let start = markers.get(start).unwrap()[0].to_display_point(&snapshot);
11079 let end = markers.get(end).unwrap()[0].to_display_point(&snapshot);
11080 start..end
11081 })
11082 .collect();
11083 assert_eq!(
11084 view.selections.display_ranges(cx),
11085 &asserted_ranges[..],
11086 "Assert selections are {}",
11087 marked_text
11088 );
11089 }
11090}
11091
11092trait RangeExt<T> {
11093 fn sorted(&self) -> Range<T>;
11094 fn to_inclusive(&self) -> RangeInclusive<T>;
11095}
11096
11097impl<T: Ord + Clone> RangeExt<T> for Range<T> {
11098 fn sorted(&self) -> Self {
11099 cmp::min(&self.start, &self.end).clone()..cmp::max(&self.start, &self.end).clone()
11100 }
11101
11102 fn to_inclusive(&self) -> RangeInclusive<T> {
11103 self.start.clone()..=self.end.clone()
11104 }
11105}
11106
11107trait RangeToAnchorExt {
11108 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
11109}
11110
11111impl<T: ToOffset> RangeToAnchorExt for Range<T> {
11112 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
11113 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
11114 }
11115}