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 if selections.iter().all(|s| s.is_empty()) {
2925 self.transact(cx, |this, cx| {
2926 this.buffer.update(cx, |buffer, cx| {
2927 let mut prev_cursor_row = 0;
2928 let mut row_delta = 0;
2929 for selection in &mut selections {
2930 let mut cursor = selection.start;
2931 if cursor.row != prev_cursor_row {
2932 row_delta = 0;
2933 prev_cursor_row = cursor.row;
2934 }
2935 cursor.column += row_delta;
2936
2937 let language_name = buffer.language_at(cursor, cx).map(|l| l.name());
2938 let settings = cx.global::<Settings>();
2939 let tab_size = if settings.hard_tabs(language_name.as_deref()) {
2940 IndentSize::tab()
2941 } else {
2942 let tab_size = settings.tab_size(language_name.as_deref()).get();
2943 let char_column = buffer
2944 .read(cx)
2945 .text_for_range(Point::new(cursor.row, 0)..cursor)
2946 .flat_map(str::chars)
2947 .count();
2948 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
2949 IndentSize::spaces(chars_to_next_tab_stop)
2950 };
2951 buffer.edit(
2952 [(cursor..cursor, tab_size.chars().collect::<String>())],
2953 None,
2954 cx,
2955 );
2956 cursor.column += tab_size.len;
2957 selection.start = cursor;
2958 selection.end = cursor;
2959
2960 row_delta += tab_size.len;
2961 }
2962 });
2963 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
2964 s.select(selections);
2965 });
2966 });
2967 } else {
2968 self.indent(&Indent, cx);
2969 }
2970 }
2971
2972 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
2973 let mut selections = self.selections.all::<Point>(cx);
2974 self.transact(cx, |this, cx| {
2975 let mut last_indent = None;
2976 this.buffer.update(cx, |buffer, cx| {
2977 let snapshot = buffer.snapshot(cx);
2978 for selection in &mut selections {
2979 let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
2980 let settings = &cx.global::<Settings>();
2981 let tab_size = settings.tab_size(language_name.as_deref()).get();
2982 let indent_kind = if settings.hard_tabs(language_name.as_deref()) {
2983 IndentKind::Tab
2984 } else {
2985 IndentKind::Space
2986 };
2987
2988 let mut start_row = selection.start.row;
2989 let mut end_row = selection.end.row + 1;
2990
2991 // If a selection ends at the beginning of a line, don't indent
2992 // that last line.
2993 if selection.end.column == 0 {
2994 end_row -= 1;
2995 }
2996
2997 // Avoid re-indenting a row that has already been indented by a
2998 // previous selection, but still update this selection's column
2999 // to reflect that indentation.
3000 if let Some((last_indent_row, last_indent_len)) = last_indent {
3001 if last_indent_row == selection.start.row {
3002 selection.start.column += last_indent_len;
3003 start_row += 1;
3004 }
3005 if last_indent_row == selection.end.row {
3006 selection.end.column += last_indent_len;
3007 }
3008 }
3009
3010 for row in start_row..end_row {
3011 let current_indent = snapshot.indent_size_for_line(row);
3012 let indent_delta = match (current_indent.kind, indent_kind) {
3013 (IndentKind::Space, IndentKind::Space) => {
3014 let columns_to_next_tab_stop =
3015 tab_size - (current_indent.len % tab_size);
3016 IndentSize::spaces(columns_to_next_tab_stop)
3017 }
3018 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
3019 (_, IndentKind::Tab) => IndentSize::tab(),
3020 };
3021
3022 let row_start = Point::new(row, 0);
3023 buffer.edit(
3024 [(
3025 row_start..row_start,
3026 indent_delta.chars().collect::<String>(),
3027 )],
3028 None,
3029 cx,
3030 );
3031
3032 // Update this selection's endpoints to reflect the indentation.
3033 if row == selection.start.row {
3034 selection.start.column += indent_delta.len;
3035 }
3036 if row == selection.end.row {
3037 selection.end.column += indent_delta.len as u32;
3038 }
3039
3040 last_indent = Some((row, indent_delta.len));
3041 }
3042 }
3043 });
3044
3045 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3046 s.select(selections);
3047 });
3048 });
3049 }
3050
3051 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
3052 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3053 let selections = self.selections.all::<Point>(cx);
3054 let mut deletion_ranges = Vec::new();
3055 let mut last_outdent = None;
3056 {
3057 let buffer = self.buffer.read(cx);
3058 let snapshot = buffer.snapshot(cx);
3059 for selection in &selections {
3060 let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
3061 let tab_size = cx
3062 .global::<Settings>()
3063 .tab_size(language_name.as_deref())
3064 .get();
3065 let mut rows = selection.spanned_rows(false, &display_map);
3066
3067 // Avoid re-outdenting a row that has already been outdented by a
3068 // previous selection.
3069 if let Some(last_row) = last_outdent {
3070 if last_row == rows.start {
3071 rows.start += 1;
3072 }
3073 }
3074
3075 for row in rows {
3076 let indent_size = snapshot.indent_size_for_line(row);
3077 if indent_size.len > 0 {
3078 let deletion_len = match indent_size.kind {
3079 IndentKind::Space => {
3080 let columns_to_prev_tab_stop = indent_size.len % tab_size;
3081 if columns_to_prev_tab_stop == 0 {
3082 tab_size
3083 } else {
3084 columns_to_prev_tab_stop
3085 }
3086 }
3087 IndentKind::Tab => 1,
3088 };
3089 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
3090 last_outdent = Some(row);
3091 }
3092 }
3093 }
3094 }
3095
3096 self.transact(cx, |this, cx| {
3097 this.buffer.update(cx, |buffer, cx| {
3098 let empty_str: Arc<str> = "".into();
3099 buffer.edit(
3100 deletion_ranges
3101 .into_iter()
3102 .map(|range| (range, empty_str.clone())),
3103 None,
3104 cx,
3105 );
3106 });
3107 let selections = this.selections.all::<usize>(cx);
3108 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
3109 });
3110 }
3111
3112 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
3113 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3114 let selections = self.selections.all::<Point>(cx);
3115
3116 let mut new_cursors = Vec::new();
3117 let mut edit_ranges = Vec::new();
3118 let mut selections = selections.iter().peekable();
3119 while let Some(selection) = selections.next() {
3120 let mut rows = selection.spanned_rows(false, &display_map);
3121 let goal_display_column = selection.head().to_display_point(&display_map).column();
3122
3123 // Accumulate contiguous regions of rows that we want to delete.
3124 while let Some(next_selection) = selections.peek() {
3125 let next_rows = next_selection.spanned_rows(false, &display_map);
3126 if next_rows.start <= rows.end {
3127 rows.end = next_rows.end;
3128 selections.next().unwrap();
3129 } else {
3130 break;
3131 }
3132 }
3133
3134 let buffer = &display_map.buffer_snapshot;
3135 let mut edit_start = Point::new(rows.start, 0).to_offset(&buffer);
3136 let edit_end;
3137 let cursor_buffer_row;
3138 if buffer.max_point().row >= rows.end {
3139 // If there's a line after the range, delete the \n from the end of the row range
3140 // and position the cursor on the next line.
3141 edit_end = Point::new(rows.end, 0).to_offset(&buffer);
3142 cursor_buffer_row = rows.end;
3143 } else {
3144 // If there isn't a line after the range, delete the \n from the line before the
3145 // start of the row range and position the cursor there.
3146 edit_start = edit_start.saturating_sub(1);
3147 edit_end = buffer.len();
3148 cursor_buffer_row = rows.start.saturating_sub(1);
3149 }
3150
3151 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
3152 *cursor.column_mut() =
3153 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
3154
3155 new_cursors.push((
3156 selection.id,
3157 buffer.anchor_after(cursor.to_point(&display_map)),
3158 ));
3159 edit_ranges.push(edit_start..edit_end);
3160 }
3161
3162 self.transact(cx, |this, cx| {
3163 let buffer = this.buffer.update(cx, |buffer, cx| {
3164 let empty_str: Arc<str> = "".into();
3165 buffer.edit(
3166 edit_ranges
3167 .into_iter()
3168 .map(|range| (range, empty_str.clone())),
3169 None,
3170 cx,
3171 );
3172 buffer.snapshot(cx)
3173 });
3174 let new_selections = new_cursors
3175 .into_iter()
3176 .map(|(id, cursor)| {
3177 let cursor = cursor.to_point(&buffer);
3178 Selection {
3179 id,
3180 start: cursor,
3181 end: cursor,
3182 reversed: false,
3183 goal: SelectionGoal::None,
3184 }
3185 })
3186 .collect();
3187
3188 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3189 s.select(new_selections);
3190 });
3191 });
3192 }
3193
3194 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
3195 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3196 let buffer = &display_map.buffer_snapshot;
3197 let selections = self.selections.all::<Point>(cx);
3198
3199 let mut edits = Vec::new();
3200 let mut selections_iter = selections.iter().peekable();
3201 while let Some(selection) = selections_iter.next() {
3202 // Avoid duplicating the same lines twice.
3203 let mut rows = selection.spanned_rows(false, &display_map);
3204
3205 while let Some(next_selection) = selections_iter.peek() {
3206 let next_rows = next_selection.spanned_rows(false, &display_map);
3207 if next_rows.start <= rows.end - 1 {
3208 rows.end = next_rows.end;
3209 selections_iter.next().unwrap();
3210 } else {
3211 break;
3212 }
3213 }
3214
3215 // Copy the text from the selected row region and splice it at the start of the region.
3216 let start = Point::new(rows.start, 0);
3217 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
3218 let text = buffer
3219 .text_for_range(start..end)
3220 .chain(Some("\n"))
3221 .collect::<String>();
3222 edits.push((start..start, text));
3223 }
3224
3225 self.transact(cx, |this, cx| {
3226 this.buffer.update(cx, |buffer, cx| {
3227 buffer.edit(edits, None, cx);
3228 });
3229
3230 this.request_autoscroll(Autoscroll::Fit, cx);
3231 });
3232 }
3233
3234 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
3235 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3236 let buffer = self.buffer.read(cx).snapshot(cx);
3237
3238 let mut edits = Vec::new();
3239 let mut unfold_ranges = Vec::new();
3240 let mut refold_ranges = Vec::new();
3241
3242 let selections = self.selections.all::<Point>(cx);
3243 let mut selections = selections.iter().peekable();
3244 let mut contiguous_row_selections = Vec::new();
3245 let mut new_selections = Vec::new();
3246
3247 while let Some(selection) = selections.next() {
3248 // Find all the selections that span a contiguous row range
3249 contiguous_row_selections.push(selection.clone());
3250 let start_row = selection.start.row;
3251 let mut end_row = if selection.end.column > 0 || selection.is_empty() {
3252 display_map.next_line_boundary(selection.end).0.row + 1
3253 } else {
3254 selection.end.row
3255 };
3256
3257 while let Some(next_selection) = selections.peek() {
3258 if next_selection.start.row <= end_row {
3259 end_row = if next_selection.end.column > 0 || next_selection.is_empty() {
3260 display_map.next_line_boundary(next_selection.end).0.row + 1
3261 } else {
3262 next_selection.end.row
3263 };
3264 contiguous_row_selections.push(selections.next().unwrap().clone());
3265 } else {
3266 break;
3267 }
3268 }
3269
3270 // Move the text spanned by the row range to be before the line preceding the row range
3271 if start_row > 0 {
3272 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
3273 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
3274 let insertion_point = display_map
3275 .prev_line_boundary(Point::new(start_row - 1, 0))
3276 .0;
3277
3278 // Don't move lines across excerpts
3279 if buffer
3280 .excerpt_boundaries_in_range((
3281 Bound::Excluded(insertion_point),
3282 Bound::Included(range_to_move.end),
3283 ))
3284 .next()
3285 .is_none()
3286 {
3287 let text = buffer
3288 .text_for_range(range_to_move.clone())
3289 .flat_map(|s| s.chars())
3290 .skip(1)
3291 .chain(['\n'])
3292 .collect::<String>();
3293
3294 edits.push((
3295 buffer.anchor_after(range_to_move.start)
3296 ..buffer.anchor_before(range_to_move.end),
3297 String::new(),
3298 ));
3299 let insertion_anchor = buffer.anchor_after(insertion_point);
3300 edits.push((insertion_anchor.clone()..insertion_anchor, text));
3301
3302 let row_delta = range_to_move.start.row - insertion_point.row + 1;
3303
3304 // Move selections up
3305 new_selections.extend(contiguous_row_selections.drain(..).map(
3306 |mut selection| {
3307 selection.start.row -= row_delta;
3308 selection.end.row -= row_delta;
3309 selection
3310 },
3311 ));
3312
3313 // Move folds up
3314 unfold_ranges.push(range_to_move.clone());
3315 for fold in display_map.folds_in_range(
3316 buffer.anchor_before(range_to_move.start)
3317 ..buffer.anchor_after(range_to_move.end),
3318 ) {
3319 let mut start = fold.start.to_point(&buffer);
3320 let mut end = fold.end.to_point(&buffer);
3321 start.row -= row_delta;
3322 end.row -= row_delta;
3323 refold_ranges.push(start..end);
3324 }
3325 }
3326 }
3327
3328 // If we didn't move line(s), preserve the existing selections
3329 new_selections.extend(contiguous_row_selections.drain(..));
3330 }
3331
3332 self.transact(cx, |this, cx| {
3333 this.unfold_ranges(unfold_ranges, true, cx);
3334 this.buffer.update(cx, |buffer, cx| {
3335 for (range, text) in edits {
3336 buffer.edit([(range, text)], None, cx);
3337 }
3338 });
3339 this.fold_ranges(refold_ranges, cx);
3340 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3341 s.select(new_selections);
3342 })
3343 });
3344 }
3345
3346 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
3347 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3348 let buffer = self.buffer.read(cx).snapshot(cx);
3349
3350 let mut edits = Vec::new();
3351 let mut unfold_ranges = Vec::new();
3352 let mut refold_ranges = Vec::new();
3353
3354 let selections = self.selections.all::<Point>(cx);
3355 let mut selections = selections.iter().peekable();
3356 let mut contiguous_row_selections = Vec::new();
3357 let mut new_selections = Vec::new();
3358
3359 while let Some(selection) = selections.next() {
3360 // Find all the selections that span a contiguous row range
3361 contiguous_row_selections.push(selection.clone());
3362 let start_row = selection.start.row;
3363 let mut end_row = if selection.end.column > 0 || selection.is_empty() {
3364 display_map.next_line_boundary(selection.end).0.row + 1
3365 } else {
3366 selection.end.row
3367 };
3368
3369 while let Some(next_selection) = selections.peek() {
3370 if next_selection.start.row <= end_row {
3371 end_row = if next_selection.end.column > 0 || next_selection.is_empty() {
3372 display_map.next_line_boundary(next_selection.end).0.row + 1
3373 } else {
3374 next_selection.end.row
3375 };
3376 contiguous_row_selections.push(selections.next().unwrap().clone());
3377 } else {
3378 break;
3379 }
3380 }
3381
3382 // Move the text spanned by the row range to be after the last line of the row range
3383 if end_row <= buffer.max_point().row {
3384 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
3385 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
3386
3387 // Don't move lines across excerpt boundaries
3388 if buffer
3389 .excerpt_boundaries_in_range((
3390 Bound::Excluded(range_to_move.start),
3391 Bound::Included(insertion_point),
3392 ))
3393 .next()
3394 .is_none()
3395 {
3396 let mut text = String::from("\n");
3397 text.extend(buffer.text_for_range(range_to_move.clone()));
3398 text.pop(); // Drop trailing newline
3399 edits.push((
3400 buffer.anchor_after(range_to_move.start)
3401 ..buffer.anchor_before(range_to_move.end),
3402 String::new(),
3403 ));
3404 let insertion_anchor = buffer.anchor_after(insertion_point);
3405 edits.push((insertion_anchor.clone()..insertion_anchor, text));
3406
3407 let row_delta = insertion_point.row - range_to_move.end.row + 1;
3408
3409 // Move selections down
3410 new_selections.extend(contiguous_row_selections.drain(..).map(
3411 |mut selection| {
3412 selection.start.row += row_delta;
3413 selection.end.row += row_delta;
3414 selection
3415 },
3416 ));
3417
3418 // Move folds down
3419 unfold_ranges.push(range_to_move.clone());
3420 for fold in display_map.folds_in_range(
3421 buffer.anchor_before(range_to_move.start)
3422 ..buffer.anchor_after(range_to_move.end),
3423 ) {
3424 let mut start = fold.start.to_point(&buffer);
3425 let mut end = fold.end.to_point(&buffer);
3426 start.row += row_delta;
3427 end.row += row_delta;
3428 refold_ranges.push(start..end);
3429 }
3430 }
3431 }
3432
3433 // If we didn't move line(s), preserve the existing selections
3434 new_selections.extend(contiguous_row_selections.drain(..));
3435 }
3436
3437 self.transact(cx, |this, cx| {
3438 this.unfold_ranges(unfold_ranges, true, cx);
3439 this.buffer.update(cx, |buffer, cx| {
3440 for (range, text) in edits {
3441 buffer.edit([(range, text)], None, cx);
3442 }
3443 });
3444 this.fold_ranges(refold_ranges, cx);
3445 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(new_selections));
3446 });
3447 }
3448
3449 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
3450 self.transact(cx, |this, cx| {
3451 let edits = this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3452 let mut edits: Vec<(Range<usize>, String)> = Default::default();
3453 let line_mode = s.line_mode;
3454 s.move_with(|display_map, selection| {
3455 if !selection.is_empty() || line_mode {
3456 return;
3457 }
3458
3459 let mut head = selection.head();
3460 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
3461 if head.column() == display_map.line_len(head.row()) {
3462 transpose_offset = display_map
3463 .buffer_snapshot
3464 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
3465 }
3466
3467 if transpose_offset == 0 {
3468 return;
3469 }
3470
3471 *head.column_mut() += 1;
3472 head = display_map.clip_point(head, Bias::Right);
3473 selection.collapse_to(head, SelectionGoal::Column(head.column()));
3474
3475 let transpose_start = display_map
3476 .buffer_snapshot
3477 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
3478 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
3479 let transpose_end = display_map
3480 .buffer_snapshot
3481 .clip_offset(transpose_offset + 1, Bias::Right);
3482 if let Some(ch) =
3483 display_map.buffer_snapshot.chars_at(transpose_start).next()
3484 {
3485 edits.push((transpose_start..transpose_offset, String::new()));
3486 edits.push((transpose_end..transpose_end, ch.to_string()));
3487 }
3488 }
3489 });
3490 edits
3491 });
3492 this.buffer
3493 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
3494 let selections = this.selections.all::<usize>(cx);
3495 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3496 s.select(selections);
3497 });
3498 });
3499 }
3500
3501 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
3502 let mut text = String::new();
3503 let buffer = self.buffer.read(cx).snapshot(cx);
3504 let mut selections = self.selections.all::<Point>(cx);
3505 let mut clipboard_selections = Vec::with_capacity(selections.len());
3506 {
3507 let max_point = buffer.max_point();
3508 for selection in &mut selections {
3509 let is_entire_line = selection.is_empty() || self.selections.line_mode;
3510 if is_entire_line {
3511 selection.start = Point::new(selection.start.row, 0);
3512 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
3513 selection.goal = SelectionGoal::None;
3514 }
3515 let mut len = 0;
3516 for chunk in buffer.text_for_range(selection.start..selection.end) {
3517 text.push_str(chunk);
3518 len += chunk.len();
3519 }
3520 clipboard_selections.push(ClipboardSelection {
3521 len,
3522 is_entire_line,
3523 first_line_indent: buffer.indent_size_for_line(selection.start.row).len,
3524 });
3525 }
3526 }
3527
3528 self.transact(cx, |this, cx| {
3529 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3530 s.select(selections);
3531 });
3532 this.insert("", cx);
3533 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
3534 });
3535 }
3536
3537 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
3538 let selections = self.selections.all::<Point>(cx);
3539 let buffer = self.buffer.read(cx).read(cx);
3540 let mut text = String::new();
3541
3542 let mut clipboard_selections = Vec::with_capacity(selections.len());
3543 {
3544 let max_point = buffer.max_point();
3545 for selection in selections.iter() {
3546 let mut start = selection.start;
3547 let mut end = selection.end;
3548 let is_entire_line = selection.is_empty() || self.selections.line_mode;
3549 if is_entire_line {
3550 start = Point::new(start.row, 0);
3551 end = cmp::min(max_point, Point::new(end.row + 1, 0));
3552 }
3553 let mut len = 0;
3554 for chunk in buffer.text_for_range(start..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(start.row).len,
3562 });
3563 }
3564 }
3565
3566 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
3567 }
3568
3569 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
3570 self.transact(cx, |this, cx| {
3571 if let Some(item) = cx.as_mut().read_from_clipboard() {
3572 let mut clipboard_text = Cow::Borrowed(item.text());
3573 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
3574 let old_selections = this.selections.all::<usize>(cx);
3575 let all_selections_were_entire_line =
3576 clipboard_selections.iter().all(|s| s.is_entire_line);
3577 if clipboard_selections.len() != old_selections.len() {
3578 let mut newline_separated_text = String::new();
3579 let mut clipboard_selections = clipboard_selections.drain(..).peekable();
3580 let mut ix = 0;
3581 while let Some(clipboard_selection) = clipboard_selections.next() {
3582 newline_separated_text
3583 .push_str(&clipboard_text[ix..ix + clipboard_selection.len]);
3584 ix += clipboard_selection.len;
3585 if clipboard_selections.peek().is_some() {
3586 newline_separated_text.push('\n');
3587 }
3588 }
3589 clipboard_text = Cow::Owned(newline_separated_text);
3590 }
3591
3592 this.buffer.update(cx, |buffer, cx| {
3593 let snapshot = buffer.read(cx);
3594 let mut start_offset = 0;
3595 let mut edits = Vec::new();
3596 let mut start_columns = Vec::new();
3597 let line_mode = this.selections.line_mode;
3598 for (ix, selection) in old_selections.iter().enumerate() {
3599 let to_insert;
3600 let entire_line;
3601 let start_column;
3602 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
3603 let end_offset = start_offset + clipboard_selection.len;
3604 to_insert = &clipboard_text[start_offset..end_offset];
3605 entire_line = clipboard_selection.is_entire_line;
3606 start_offset = end_offset;
3607 start_column = clipboard_selection.first_line_indent;
3608 } else {
3609 to_insert = clipboard_text.as_str();
3610 entire_line = all_selections_were_entire_line;
3611 start_column = 0;
3612 }
3613
3614 // If the corresponding selection was empty when this slice of the
3615 // clipboard text was written, then the entire line containing the
3616 // selection was copied. If this selection is also currently empty,
3617 // then paste the line before the current line of the buffer.
3618 let range = if selection.is_empty() && !line_mode && entire_line {
3619 let column = selection.start.to_point(&snapshot).column as usize;
3620 let line_start = selection.start - column;
3621 line_start..line_start
3622 } else {
3623 selection.range()
3624 };
3625
3626 edits.push((range, to_insert));
3627 start_columns.push(start_column);
3628 }
3629 drop(snapshot);
3630 buffer.edit(
3631 edits,
3632 Some(AutoindentMode::Block {
3633 original_indent_columns: start_columns,
3634 }),
3635 cx,
3636 );
3637 });
3638
3639 let selections = this.selections.all::<usize>(cx);
3640 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
3641 } else {
3642 this.insert(&clipboard_text, cx);
3643 }
3644 }
3645 });
3646 }
3647
3648 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
3649 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
3650 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
3651 self.change_selections(None, cx, |s| {
3652 s.select_anchors(selections.to_vec());
3653 });
3654 }
3655 self.request_autoscroll(Autoscroll::Fit, cx);
3656 self.unmark_text(cx);
3657 cx.emit(Event::Edited);
3658 }
3659 }
3660
3661 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
3662 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
3663 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
3664 {
3665 self.change_selections(None, cx, |s| {
3666 s.select_anchors(selections.to_vec());
3667 });
3668 }
3669 self.request_autoscroll(Autoscroll::Fit, cx);
3670 self.unmark_text(cx);
3671 cx.emit(Event::Edited);
3672 }
3673 }
3674
3675 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
3676 self.buffer
3677 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
3678 }
3679
3680 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
3681 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3682 let line_mode = s.line_mode;
3683 s.move_with(|map, selection| {
3684 let cursor = if selection.is_empty() && !line_mode {
3685 movement::left(map, selection.start)
3686 } else {
3687 selection.start
3688 };
3689 selection.collapse_to(cursor, SelectionGoal::None);
3690 });
3691 })
3692 }
3693
3694 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
3695 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3696 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
3697 })
3698 }
3699
3700 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
3701 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3702 let line_mode = s.line_mode;
3703 s.move_with(|map, selection| {
3704 let cursor = if selection.is_empty() && !line_mode {
3705 movement::right(map, selection.end)
3706 } else {
3707 selection.end
3708 };
3709 selection.collapse_to(cursor, SelectionGoal::None)
3710 });
3711 })
3712 }
3713
3714 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
3715 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3716 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
3717 })
3718 }
3719
3720 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
3721 if self.take_rename(true, cx).is_some() {
3722 return;
3723 }
3724
3725 if let Some(context_menu) = self.context_menu.as_mut() {
3726 if context_menu.select_prev(cx) {
3727 return;
3728 }
3729 }
3730
3731 if matches!(self.mode, EditorMode::SingleLine) {
3732 cx.propagate_action();
3733 return;
3734 }
3735
3736 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3737 let line_mode = s.line_mode;
3738 s.move_with(|map, selection| {
3739 if !selection.is_empty() && !line_mode {
3740 selection.goal = SelectionGoal::None;
3741 }
3742 let (cursor, goal) = movement::up(&map, selection.start, selection.goal, false);
3743 selection.collapse_to(cursor, goal);
3744 });
3745 })
3746 }
3747
3748 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
3749 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3750 s.move_heads_with(|map, head, goal| movement::up(map, head, goal, false))
3751 })
3752 }
3753
3754 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
3755 self.take_rename(true, cx);
3756
3757 if let Some(context_menu) = self.context_menu.as_mut() {
3758 if context_menu.select_next(cx) {
3759 return;
3760 }
3761 }
3762
3763 if matches!(self.mode, EditorMode::SingleLine) {
3764 cx.propagate_action();
3765 return;
3766 }
3767
3768 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3769 let line_mode = s.line_mode;
3770 s.move_with(|map, selection| {
3771 if !selection.is_empty() && !line_mode {
3772 selection.goal = SelectionGoal::None;
3773 }
3774 let (cursor, goal) = movement::down(&map, selection.end, selection.goal, false);
3775 selection.collapse_to(cursor, goal);
3776 });
3777 });
3778 }
3779
3780 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
3781 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3782 s.move_heads_with(|map, head, goal| movement::down(map, head, goal, false))
3783 });
3784 }
3785
3786 pub fn move_to_previous_word_start(
3787 &mut self,
3788 _: &MoveToPreviousWordStart,
3789 cx: &mut ViewContext<Self>,
3790 ) {
3791 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3792 s.move_cursors_with(|map, head, _| {
3793 (
3794 movement::previous_word_start(map, head),
3795 SelectionGoal::None,
3796 )
3797 });
3798 })
3799 }
3800
3801 pub fn move_to_previous_subword_start(
3802 &mut self,
3803 _: &MoveToPreviousSubwordStart,
3804 cx: &mut ViewContext<Self>,
3805 ) {
3806 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3807 s.move_cursors_with(|map, head, _| {
3808 (
3809 movement::previous_subword_start(map, head),
3810 SelectionGoal::None,
3811 )
3812 });
3813 })
3814 }
3815
3816 pub fn select_to_previous_word_start(
3817 &mut self,
3818 _: &SelectToPreviousWordStart,
3819 cx: &mut ViewContext<Self>,
3820 ) {
3821 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3822 s.move_heads_with(|map, head, _| {
3823 (
3824 movement::previous_word_start(map, head),
3825 SelectionGoal::None,
3826 )
3827 });
3828 })
3829 }
3830
3831 pub fn select_to_previous_subword_start(
3832 &mut self,
3833 _: &SelectToPreviousSubwordStart,
3834 cx: &mut ViewContext<Self>,
3835 ) {
3836 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3837 s.move_heads_with(|map, head, _| {
3838 (
3839 movement::previous_subword_start(map, head),
3840 SelectionGoal::None,
3841 )
3842 });
3843 })
3844 }
3845
3846 pub fn delete_to_previous_word_start(
3847 &mut self,
3848 _: &DeleteToPreviousWordStart,
3849 cx: &mut ViewContext<Self>,
3850 ) {
3851 self.transact(cx, |this, cx| {
3852 if !this.select_autoclose_pair(cx) {
3853 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3854 let line_mode = s.line_mode;
3855 s.move_with(|map, selection| {
3856 if selection.is_empty() && !line_mode {
3857 let cursor = movement::previous_word_start(map, selection.head());
3858 selection.set_head(cursor, SelectionGoal::None);
3859 }
3860 });
3861 });
3862 }
3863 this.insert("", cx);
3864 });
3865 }
3866
3867 pub fn delete_to_previous_subword_start(
3868 &mut self,
3869 _: &DeleteToPreviousSubwordStart,
3870 cx: &mut ViewContext<Self>,
3871 ) {
3872 self.transact(cx, |this, cx| {
3873 if !this.select_autoclose_pair(cx) {
3874 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3875 let line_mode = s.line_mode;
3876 s.move_with(|map, selection| {
3877 if selection.is_empty() && !line_mode {
3878 let cursor = movement::previous_subword_start(map, selection.head());
3879 selection.set_head(cursor, SelectionGoal::None);
3880 }
3881 });
3882 });
3883 }
3884 this.insert("", cx);
3885 });
3886 }
3887
3888 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
3889 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3890 s.move_cursors_with(|map, head, _| {
3891 (movement::next_word_end(map, head), SelectionGoal::None)
3892 });
3893 })
3894 }
3895
3896 pub fn move_to_next_subword_end(
3897 &mut self,
3898 _: &MoveToNextSubwordEnd,
3899 cx: &mut ViewContext<Self>,
3900 ) {
3901 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3902 s.move_cursors_with(|map, head, _| {
3903 (movement::next_subword_end(map, head), SelectionGoal::None)
3904 });
3905 })
3906 }
3907
3908 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
3909 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3910 s.move_heads_with(|map, head, _| {
3911 (movement::next_word_end(map, head), SelectionGoal::None)
3912 });
3913 })
3914 }
3915
3916 pub fn select_to_next_subword_end(
3917 &mut self,
3918 _: &SelectToNextSubwordEnd,
3919 cx: &mut ViewContext<Self>,
3920 ) {
3921 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3922 s.move_heads_with(|map, head, _| {
3923 (movement::next_subword_end(map, head), SelectionGoal::None)
3924 });
3925 })
3926 }
3927
3928 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
3929 self.transact(cx, |this, cx| {
3930 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3931 let line_mode = s.line_mode;
3932 s.move_with(|map, selection| {
3933 if selection.is_empty() && !line_mode {
3934 let cursor = movement::next_word_end(map, selection.head());
3935 selection.set_head(cursor, SelectionGoal::None);
3936 }
3937 });
3938 });
3939 this.insert("", cx);
3940 });
3941 }
3942
3943 pub fn delete_to_next_subword_end(
3944 &mut self,
3945 _: &DeleteToNextSubwordEnd,
3946 cx: &mut ViewContext<Self>,
3947 ) {
3948 self.transact(cx, |this, cx| {
3949 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3950 s.move_with(|map, selection| {
3951 if selection.is_empty() {
3952 let cursor = movement::next_subword_end(map, selection.head());
3953 selection.set_head(cursor, SelectionGoal::None);
3954 }
3955 });
3956 });
3957 this.insert("", cx);
3958 });
3959 }
3960
3961 pub fn move_to_beginning_of_line(
3962 &mut self,
3963 _: &MoveToBeginningOfLine,
3964 cx: &mut ViewContext<Self>,
3965 ) {
3966 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3967 s.move_cursors_with(|map, head, _| {
3968 (
3969 movement::line_beginning(map, head, true),
3970 SelectionGoal::None,
3971 )
3972 });
3973 })
3974 }
3975
3976 pub fn select_to_beginning_of_line(
3977 &mut self,
3978 action: &SelectToBeginningOfLine,
3979 cx: &mut ViewContext<Self>,
3980 ) {
3981 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3982 s.move_heads_with(|map, head, _| {
3983 (
3984 movement::line_beginning(map, head, action.stop_at_soft_wraps),
3985 SelectionGoal::None,
3986 )
3987 });
3988 });
3989 }
3990
3991 pub fn delete_to_beginning_of_line(
3992 &mut self,
3993 _: &DeleteToBeginningOfLine,
3994 cx: &mut ViewContext<Self>,
3995 ) {
3996 self.transact(cx, |this, cx| {
3997 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3998 s.move_with(|_, selection| {
3999 selection.reversed = true;
4000 });
4001 });
4002
4003 this.select_to_beginning_of_line(
4004 &SelectToBeginningOfLine {
4005 stop_at_soft_wraps: false,
4006 },
4007 cx,
4008 );
4009 this.backspace(&Backspace, cx);
4010 });
4011 }
4012
4013 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
4014 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4015 s.move_cursors_with(|map, head, _| {
4016 (movement::line_end(map, head, true), SelectionGoal::None)
4017 });
4018 })
4019 }
4020
4021 pub fn select_to_end_of_line(
4022 &mut self,
4023 action: &SelectToEndOfLine,
4024 cx: &mut ViewContext<Self>,
4025 ) {
4026 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4027 s.move_heads_with(|map, head, _| {
4028 (
4029 movement::line_end(map, head, action.stop_at_soft_wraps),
4030 SelectionGoal::None,
4031 )
4032 });
4033 })
4034 }
4035
4036 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
4037 self.transact(cx, |this, cx| {
4038 this.select_to_end_of_line(
4039 &SelectToEndOfLine {
4040 stop_at_soft_wraps: false,
4041 },
4042 cx,
4043 );
4044 this.delete(&Delete, cx);
4045 });
4046 }
4047
4048 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
4049 self.transact(cx, |this, cx| {
4050 this.select_to_end_of_line(
4051 &SelectToEndOfLine {
4052 stop_at_soft_wraps: false,
4053 },
4054 cx,
4055 );
4056 this.cut(&Cut, cx);
4057 });
4058 }
4059
4060 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
4061 if matches!(self.mode, EditorMode::SingleLine) {
4062 cx.propagate_action();
4063 return;
4064 }
4065
4066 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4067 s.select_ranges(vec![0..0]);
4068 });
4069 }
4070
4071 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
4072 let mut selection = self.selections.last::<Point>(cx);
4073 selection.set_head(Point::zero(), SelectionGoal::None);
4074
4075 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4076 s.select(vec![selection]);
4077 });
4078 }
4079
4080 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
4081 if matches!(self.mode, EditorMode::SingleLine) {
4082 cx.propagate_action();
4083 return;
4084 }
4085
4086 let cursor = self.buffer.read(cx).read(cx).len();
4087 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4088 s.select_ranges(vec![cursor..cursor])
4089 });
4090 }
4091
4092 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
4093 self.nav_history = nav_history;
4094 }
4095
4096 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
4097 self.nav_history.as_ref()
4098 }
4099
4100 fn push_to_nav_history(
4101 &self,
4102 position: Anchor,
4103 new_position: Option<Point>,
4104 cx: &mut ViewContext<Self>,
4105 ) {
4106 if let Some(nav_history) = &self.nav_history {
4107 let buffer = self.buffer.read(cx).read(cx);
4108 let point = position.to_point(&buffer);
4109 let scroll_top_row = self.scroll_top_anchor.to_point(&buffer).row;
4110 drop(buffer);
4111
4112 if let Some(new_position) = new_position {
4113 let row_delta = (new_position.row as i64 - point.row as i64).abs();
4114 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
4115 return;
4116 }
4117 }
4118
4119 nav_history.push(
4120 Some(NavigationData {
4121 cursor_anchor: position,
4122 cursor_position: point,
4123 scroll_position: self.scroll_position,
4124 scroll_top_anchor: self.scroll_top_anchor.clone(),
4125 scroll_top_row,
4126 }),
4127 cx,
4128 );
4129 }
4130 }
4131
4132 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
4133 let buffer = self.buffer.read(cx).snapshot(cx);
4134 let mut selection = self.selections.first::<usize>(cx);
4135 selection.set_head(buffer.len(), SelectionGoal::None);
4136 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4137 s.select(vec![selection]);
4138 });
4139 }
4140
4141 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
4142 let end = self.buffer.read(cx).read(cx).len();
4143 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4144 s.select_ranges(vec![0..end]);
4145 });
4146 }
4147
4148 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
4149 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4150 let mut selections = self.selections.all::<Point>(cx);
4151 let max_point = display_map.buffer_snapshot.max_point();
4152 for selection in &mut selections {
4153 let rows = selection.spanned_rows(true, &display_map);
4154 selection.start = Point::new(rows.start, 0);
4155 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
4156 selection.reversed = false;
4157 }
4158 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4159 s.select(selections);
4160 });
4161 }
4162
4163 pub fn split_selection_into_lines(
4164 &mut self,
4165 _: &SplitSelectionIntoLines,
4166 cx: &mut ViewContext<Self>,
4167 ) {
4168 let mut to_unfold = Vec::new();
4169 let mut new_selection_ranges = Vec::new();
4170 {
4171 let selections = self.selections.all::<Point>(cx);
4172 let buffer = self.buffer.read(cx).read(cx);
4173 for selection in selections {
4174 for row in selection.start.row..selection.end.row {
4175 let cursor = Point::new(row, buffer.line_len(row));
4176 new_selection_ranges.push(cursor..cursor);
4177 }
4178 new_selection_ranges.push(selection.end..selection.end);
4179 to_unfold.push(selection.start..selection.end);
4180 }
4181 }
4182 self.unfold_ranges(to_unfold, true, cx);
4183 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4184 s.select_ranges(new_selection_ranges);
4185 });
4186 }
4187
4188 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
4189 self.add_selection(true, cx);
4190 }
4191
4192 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
4193 self.add_selection(false, cx);
4194 }
4195
4196 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
4197 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4198 let mut selections = self.selections.all::<Point>(cx);
4199 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
4200 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
4201 let range = oldest_selection.display_range(&display_map).sorted();
4202 let columns = cmp::min(range.start.column(), range.end.column())
4203 ..cmp::max(range.start.column(), range.end.column());
4204
4205 selections.clear();
4206 let mut stack = Vec::new();
4207 for row in range.start.row()..=range.end.row() {
4208 if let Some(selection) = self.selections.build_columnar_selection(
4209 &display_map,
4210 row,
4211 &columns,
4212 oldest_selection.reversed,
4213 ) {
4214 stack.push(selection.id);
4215 selections.push(selection);
4216 }
4217 }
4218
4219 if above {
4220 stack.reverse();
4221 }
4222
4223 AddSelectionsState { above, stack }
4224 });
4225
4226 let last_added_selection = *state.stack.last().unwrap();
4227 let mut new_selections = Vec::new();
4228 if above == state.above {
4229 let end_row = if above {
4230 0
4231 } else {
4232 display_map.max_point().row()
4233 };
4234
4235 'outer: for selection in selections {
4236 if selection.id == last_added_selection {
4237 let range = selection.display_range(&display_map).sorted();
4238 debug_assert_eq!(range.start.row(), range.end.row());
4239 let mut row = range.start.row();
4240 let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
4241 {
4242 start..end
4243 } else {
4244 cmp::min(range.start.column(), range.end.column())
4245 ..cmp::max(range.start.column(), range.end.column())
4246 };
4247
4248 while row != end_row {
4249 if above {
4250 row -= 1;
4251 } else {
4252 row += 1;
4253 }
4254
4255 if let Some(new_selection) = self.selections.build_columnar_selection(
4256 &display_map,
4257 row,
4258 &columns,
4259 selection.reversed,
4260 ) {
4261 state.stack.push(new_selection.id);
4262 if above {
4263 new_selections.push(new_selection);
4264 new_selections.push(selection);
4265 } else {
4266 new_selections.push(selection);
4267 new_selections.push(new_selection);
4268 }
4269
4270 continue 'outer;
4271 }
4272 }
4273 }
4274
4275 new_selections.push(selection);
4276 }
4277 } else {
4278 new_selections = selections;
4279 new_selections.retain(|s| s.id != last_added_selection);
4280 state.stack.pop();
4281 }
4282
4283 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4284 s.select(new_selections);
4285 });
4286 if state.stack.len() > 1 {
4287 self.add_selections_state = Some(state);
4288 }
4289 }
4290
4291 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) {
4292 self.push_to_selection_history();
4293 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4294 let buffer = &display_map.buffer_snapshot;
4295 let mut selections = self.selections.all::<usize>(cx);
4296 if let Some(mut select_next_state) = self.select_next_state.take() {
4297 let query = &select_next_state.query;
4298 if !select_next_state.done {
4299 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
4300 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
4301 let mut next_selected_range = None;
4302
4303 let bytes_after_last_selection =
4304 buffer.bytes_in_range(last_selection.end..buffer.len());
4305 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
4306 let query_matches = query
4307 .stream_find_iter(bytes_after_last_selection)
4308 .map(|result| (last_selection.end, result))
4309 .chain(
4310 query
4311 .stream_find_iter(bytes_before_first_selection)
4312 .map(|result| (0, result)),
4313 );
4314 for (start_offset, query_match) in query_matches {
4315 let query_match = query_match.unwrap(); // can only fail due to I/O
4316 let offset_range =
4317 start_offset + query_match.start()..start_offset + query_match.end();
4318 let display_range = offset_range.start.to_display_point(&display_map)
4319 ..offset_range.end.to_display_point(&display_map);
4320
4321 if !select_next_state.wordwise
4322 || (!movement::is_inside_word(&display_map, display_range.start)
4323 && !movement::is_inside_word(&display_map, display_range.end))
4324 {
4325 next_selected_range = Some(offset_range);
4326 break;
4327 }
4328 }
4329
4330 if let Some(next_selected_range) = next_selected_range {
4331 self.unfold_ranges([next_selected_range.clone()], false, cx);
4332 self.change_selections(Some(Autoscroll::Newest), cx, |s| {
4333 if action.replace_newest {
4334 s.delete(s.newest_anchor().id);
4335 }
4336 s.insert_range(next_selected_range);
4337 });
4338 } else {
4339 select_next_state.done = true;
4340 }
4341 }
4342
4343 self.select_next_state = Some(select_next_state);
4344 } else if selections.len() == 1 {
4345 let selection = selections.last_mut().unwrap();
4346 if selection.start == selection.end {
4347 let word_range = movement::surrounding_word(
4348 &display_map,
4349 selection.start.to_display_point(&display_map),
4350 );
4351 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
4352 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
4353 selection.goal = SelectionGoal::None;
4354 selection.reversed = false;
4355
4356 let query = buffer
4357 .text_for_range(selection.start..selection.end)
4358 .collect::<String>();
4359 let select_state = SelectNextState {
4360 query: AhoCorasick::new_auto_configured(&[query]),
4361 wordwise: true,
4362 done: false,
4363 };
4364 self.unfold_ranges([selection.start..selection.end], false, cx);
4365 self.change_selections(Some(Autoscroll::Newest), cx, |s| {
4366 s.select(selections);
4367 });
4368 self.select_next_state = Some(select_state);
4369 } else {
4370 let query = buffer
4371 .text_for_range(selection.start..selection.end)
4372 .collect::<String>();
4373 self.select_next_state = Some(SelectNextState {
4374 query: AhoCorasick::new_auto_configured(&[query]),
4375 wordwise: false,
4376 done: false,
4377 });
4378 self.select_next(action, cx);
4379 }
4380 }
4381 }
4382
4383 pub fn toggle_comments(&mut self, _: &ToggleComments, cx: &mut ViewContext<Self>) {
4384 self.transact(cx, |this, cx| {
4385 let mut selections = this.selections.all::<Point>(cx);
4386 let mut all_selection_lines_are_comments = true;
4387 let mut edit_ranges = Vec::new();
4388 let mut last_toggled_row = None;
4389 this.buffer.update(cx, |buffer, cx| {
4390 // TODO: Handle selections that cross excerpts
4391 for selection in &mut selections {
4392 // Get the line comment prefix. Split its trailing whitespace into a separate string,
4393 // as that portion won't be used for detecting if a line is a comment.
4394 let full_comment_prefix: Arc<str> = if let Some(prefix) = buffer
4395 .language_at(selection.start, cx)
4396 .and_then(|l| l.line_comment_prefix())
4397 {
4398 prefix.into()
4399 } else {
4400 return;
4401 };
4402 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
4403 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
4404 edit_ranges.clear();
4405 let snapshot = buffer.snapshot(cx);
4406
4407 let end_row =
4408 if selection.end.row > selection.start.row && selection.end.column == 0 {
4409 selection.end.row
4410 } else {
4411 selection.end.row + 1
4412 };
4413
4414 for row in selection.start.row..end_row {
4415 // If multiple selections contain a given row, avoid processing that
4416 // row more than once.
4417 if last_toggled_row == Some(row) {
4418 continue;
4419 } else {
4420 last_toggled_row = Some(row);
4421 }
4422
4423 if snapshot.is_line_blank(row) {
4424 continue;
4425 }
4426
4427 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
4428 let mut line_bytes = snapshot
4429 .bytes_in_range(start..snapshot.max_point())
4430 .flatten()
4431 .copied();
4432
4433 // If this line currently begins with the line comment prefix, then record
4434 // the range containing the prefix.
4435 if all_selection_lines_are_comments
4436 && line_bytes
4437 .by_ref()
4438 .take(comment_prefix.len())
4439 .eq(comment_prefix.bytes())
4440 {
4441 // Include any whitespace that matches the comment prefix.
4442 let matching_whitespace_len = line_bytes
4443 .zip(comment_prefix_whitespace.bytes())
4444 .take_while(|(a, b)| a == b)
4445 .count()
4446 as u32;
4447 let end = Point::new(
4448 row,
4449 start.column
4450 + comment_prefix.len() as u32
4451 + matching_whitespace_len,
4452 );
4453 edit_ranges.push(start..end);
4454 }
4455 // If this line does not begin with the line comment prefix, then record
4456 // the position where the prefix should be inserted.
4457 else {
4458 all_selection_lines_are_comments = false;
4459 edit_ranges.push(start..start);
4460 }
4461 }
4462
4463 if !edit_ranges.is_empty() {
4464 if all_selection_lines_are_comments {
4465 let empty_str: Arc<str> = "".into();
4466 buffer.edit(
4467 edit_ranges
4468 .iter()
4469 .cloned()
4470 .map(|range| (range, empty_str.clone())),
4471 None,
4472 cx,
4473 );
4474 } else {
4475 let min_column =
4476 edit_ranges.iter().map(|r| r.start.column).min().unwrap();
4477 let edits = edit_ranges.iter().map(|range| {
4478 let position = Point::new(range.start.row, min_column);
4479 (position..position, full_comment_prefix.clone())
4480 });
4481 buffer.edit(edits, None, cx);
4482 }
4483 }
4484 }
4485 });
4486
4487 let selections = this.selections.all::<usize>(cx);
4488 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
4489 });
4490 }
4491
4492 pub fn select_larger_syntax_node(
4493 &mut self,
4494 _: &SelectLargerSyntaxNode,
4495 cx: &mut ViewContext<Self>,
4496 ) {
4497 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4498 let buffer = self.buffer.read(cx).snapshot(cx);
4499 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
4500
4501 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
4502 let mut selected_larger_node = false;
4503 let new_selections = old_selections
4504 .iter()
4505 .map(|selection| {
4506 let old_range = selection.start..selection.end;
4507 let mut new_range = old_range.clone();
4508 while let Some(containing_range) =
4509 buffer.range_for_syntax_ancestor(new_range.clone())
4510 {
4511 new_range = containing_range;
4512 if !display_map.intersects_fold(new_range.start)
4513 && !display_map.intersects_fold(new_range.end)
4514 {
4515 break;
4516 }
4517 }
4518
4519 selected_larger_node |= new_range != old_range;
4520 Selection {
4521 id: selection.id,
4522 start: new_range.start,
4523 end: new_range.end,
4524 goal: SelectionGoal::None,
4525 reversed: selection.reversed,
4526 }
4527 })
4528 .collect::<Vec<_>>();
4529
4530 if selected_larger_node {
4531 stack.push(old_selections);
4532 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4533 s.select(new_selections);
4534 });
4535 }
4536 self.select_larger_syntax_node_stack = stack;
4537 }
4538
4539 pub fn select_smaller_syntax_node(
4540 &mut self,
4541 _: &SelectSmallerSyntaxNode,
4542 cx: &mut ViewContext<Self>,
4543 ) {
4544 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
4545 if let Some(selections) = stack.pop() {
4546 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4547 s.select(selections.to_vec());
4548 });
4549 }
4550 self.select_larger_syntax_node_stack = stack;
4551 }
4552
4553 pub fn move_to_enclosing_bracket(
4554 &mut self,
4555 _: &MoveToEnclosingBracket,
4556 cx: &mut ViewContext<Self>,
4557 ) {
4558 let buffer = self.buffer.read(cx).snapshot(cx);
4559 let mut selections = self.selections.all::<usize>(cx);
4560 for selection in &mut selections {
4561 if let Some((open_range, close_range)) =
4562 buffer.enclosing_bracket_ranges(selection.start..selection.end)
4563 {
4564 let close_range = close_range.to_inclusive();
4565 let destination = if close_range.contains(&selection.start)
4566 && close_range.contains(&selection.end)
4567 {
4568 open_range.end
4569 } else {
4570 *close_range.start()
4571 };
4572 selection.start = destination;
4573 selection.end = destination;
4574 }
4575 }
4576
4577 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4578 s.select(selections);
4579 });
4580 }
4581
4582 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
4583 self.end_selection(cx);
4584 self.selection_history.mode = SelectionHistoryMode::Undoing;
4585 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
4586 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
4587 self.select_next_state = entry.select_next_state;
4588 self.add_selections_state = entry.add_selections_state;
4589 self.request_autoscroll(Autoscroll::Newest, cx);
4590 }
4591 self.selection_history.mode = SelectionHistoryMode::Normal;
4592 }
4593
4594 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
4595 self.end_selection(cx);
4596 self.selection_history.mode = SelectionHistoryMode::Redoing;
4597 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
4598 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
4599 self.select_next_state = entry.select_next_state;
4600 self.add_selections_state = entry.add_selections_state;
4601 self.request_autoscroll(Autoscroll::Newest, cx);
4602 }
4603 self.selection_history.mode = SelectionHistoryMode::Normal;
4604 }
4605
4606 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
4607 self.go_to_diagnostic_impl(Direction::Next, cx)
4608 }
4609
4610 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
4611 self.go_to_diagnostic_impl(Direction::Prev, cx)
4612 }
4613
4614 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
4615 let buffer = self.buffer.read(cx).snapshot(cx);
4616 let selection = self.selections.newest::<usize>(cx);
4617
4618 // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
4619 if direction == Direction::Next {
4620 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
4621 let (group_id, jump_to) = popover.activation_info();
4622 self.activate_diagnostics(group_id, cx);
4623 self.change_selections(Some(Autoscroll::Center), cx, |s| {
4624 let mut new_selection = s.newest_anchor().clone();
4625 new_selection.collapse_to(jump_to, SelectionGoal::None);
4626 s.select_anchors(vec![new_selection.clone()]);
4627 });
4628 return;
4629 }
4630 }
4631
4632 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
4633 active_diagnostics
4634 .primary_range
4635 .to_offset(&buffer)
4636 .to_inclusive()
4637 });
4638 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
4639 if active_primary_range.contains(&selection.head()) {
4640 *active_primary_range.end()
4641 } else {
4642 selection.head()
4643 }
4644 } else {
4645 selection.head()
4646 };
4647
4648 loop {
4649 let mut diagnostics = if direction == Direction::Prev {
4650 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
4651 } else {
4652 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
4653 };
4654 let group = diagnostics.find_map(|entry| {
4655 if entry.diagnostic.is_primary
4656 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
4657 && !entry.range.is_empty()
4658 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
4659 {
4660 Some((entry.range, entry.diagnostic.group_id))
4661 } else {
4662 None
4663 }
4664 });
4665
4666 if let Some((primary_range, group_id)) = group {
4667 self.activate_diagnostics(group_id, cx);
4668 self.change_selections(Some(Autoscroll::Center), cx, |s| {
4669 s.select(vec![Selection {
4670 id: selection.id,
4671 start: primary_range.start,
4672 end: primary_range.start,
4673 reversed: false,
4674 goal: SelectionGoal::None,
4675 }]);
4676 });
4677 break;
4678 } else {
4679 // Cycle around to the start of the buffer, potentially moving back to the start of
4680 // the currently active diagnostic.
4681 active_primary_range.take();
4682 if direction == Direction::Prev {
4683 if search_start == buffer.len() {
4684 break;
4685 } else {
4686 search_start = buffer.len();
4687 }
4688 } else {
4689 if search_start == 0 {
4690 break;
4691 } else {
4692 search_start = 0;
4693 }
4694 }
4695 }
4696 }
4697 }
4698
4699 pub fn go_to_definition(
4700 workspace: &mut Workspace,
4701 _: &GoToDefinition,
4702 cx: &mut ViewContext<Workspace>,
4703 ) {
4704 Self::go_to_definition_of_kind(GotoDefinitionKind::Symbol, workspace, cx);
4705 }
4706
4707 pub fn go_to_type_definition(
4708 workspace: &mut Workspace,
4709 _: &GoToTypeDefinition,
4710 cx: &mut ViewContext<Workspace>,
4711 ) {
4712 Self::go_to_definition_of_kind(GotoDefinitionKind::Type, workspace, cx);
4713 }
4714
4715 fn go_to_definition_of_kind(
4716 kind: GotoDefinitionKind,
4717 workspace: &mut Workspace,
4718 cx: &mut ViewContext<Workspace>,
4719 ) {
4720 let active_item = workspace.active_item(cx);
4721 let editor_handle = if let Some(editor) = active_item
4722 .as_ref()
4723 .and_then(|item| item.act_as::<Self>(cx))
4724 {
4725 editor
4726 } else {
4727 return;
4728 };
4729
4730 let editor = editor_handle.read(cx);
4731 let buffer = editor.buffer.read(cx);
4732 let head = editor.selections.newest::<usize>(cx).head();
4733 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
4734 text_anchor
4735 } else {
4736 return;
4737 };
4738
4739 let project = workspace.project().clone();
4740 let definitions = project.update(cx, |project, cx| match kind {
4741 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
4742 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
4743 });
4744
4745 cx.spawn(|workspace, mut cx| async move {
4746 let definitions = definitions.await?;
4747 workspace.update(&mut cx, |workspace, cx| {
4748 Editor::navigate_to_definitions(workspace, editor_handle, definitions, cx);
4749 });
4750
4751 Ok::<(), anyhow::Error>(())
4752 })
4753 .detach_and_log_err(cx);
4754 }
4755
4756 pub fn navigate_to_definitions(
4757 workspace: &mut Workspace,
4758 editor_handle: ViewHandle<Editor>,
4759 definitions: Vec<LocationLink>,
4760 cx: &mut ViewContext<Workspace>,
4761 ) {
4762 let pane = workspace.active_pane().clone();
4763 for definition in definitions {
4764 let range = definition
4765 .target
4766 .range
4767 .to_offset(definition.target.buffer.read(cx));
4768
4769 let target_editor_handle = workspace.open_project_item(definition.target.buffer, cx);
4770 target_editor_handle.update(cx, |target_editor, cx| {
4771 // When selecting a definition in a different buffer, disable the nav history
4772 // to avoid creating a history entry at the previous cursor location.
4773 if editor_handle != target_editor_handle {
4774 pane.update(cx, |pane, _| pane.disable_history());
4775 }
4776 target_editor.change_selections(Some(Autoscroll::Center), cx, |s| {
4777 s.select_ranges([range]);
4778 });
4779
4780 pane.update(cx, |pane, _| pane.enable_history());
4781 });
4782 }
4783 }
4784
4785 pub fn find_all_references(
4786 workspace: &mut Workspace,
4787 _: &FindAllReferences,
4788 cx: &mut ViewContext<Workspace>,
4789 ) -> Option<Task<Result<()>>> {
4790 let active_item = workspace.active_item(cx)?;
4791 let editor_handle = active_item.act_as::<Self>(cx)?;
4792
4793 let editor = editor_handle.read(cx);
4794 let buffer = editor.buffer.read(cx);
4795 let head = editor.selections.newest::<usize>(cx).head();
4796 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
4797 let replica_id = editor.replica_id(cx);
4798
4799 let project = workspace.project().clone();
4800 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
4801 Some(cx.spawn(|workspace, mut cx| async move {
4802 let mut locations = references.await?;
4803 if locations.is_empty() {
4804 return Ok(());
4805 }
4806
4807 locations.sort_by_key(|location| location.buffer.id());
4808 let mut locations = locations.into_iter().peekable();
4809 let mut ranges_to_highlight = Vec::new();
4810
4811 let excerpt_buffer = cx.add_model(|cx| {
4812 let mut symbol_name = None;
4813 let mut multibuffer = MultiBuffer::new(replica_id);
4814 while let Some(location) = locations.next() {
4815 let buffer = location.buffer.read(cx);
4816 let mut ranges_for_buffer = Vec::new();
4817 let range = location.range.to_offset(buffer);
4818 ranges_for_buffer.push(range.clone());
4819 if symbol_name.is_none() {
4820 symbol_name = Some(buffer.text_for_range(range).collect::<String>());
4821 }
4822
4823 while let Some(next_location) = locations.peek() {
4824 if next_location.buffer == location.buffer {
4825 ranges_for_buffer.push(next_location.range.to_offset(buffer));
4826 locations.next();
4827 } else {
4828 break;
4829 }
4830 }
4831
4832 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
4833 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
4834 location.buffer.clone(),
4835 ranges_for_buffer,
4836 1,
4837 cx,
4838 ));
4839 }
4840 multibuffer.with_title(format!("References to `{}`", symbol_name.unwrap()))
4841 });
4842
4843 workspace.update(&mut cx, |workspace, cx| {
4844 let editor =
4845 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
4846 editor.update(cx, |editor, cx| {
4847 editor.highlight_background::<Self>(
4848 ranges_to_highlight,
4849 |theme| theme.editor.highlighted_line_background,
4850 cx,
4851 );
4852 });
4853 workspace.add_item(Box::new(editor), cx);
4854 });
4855
4856 Ok(())
4857 }))
4858 }
4859
4860 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
4861 use language::ToOffset as _;
4862
4863 let project = self.project.clone()?;
4864 let selection = self.selections.newest_anchor().clone();
4865 let (cursor_buffer, cursor_buffer_position) = self
4866 .buffer
4867 .read(cx)
4868 .text_anchor_for_position(selection.head(), cx)?;
4869 let (tail_buffer, _) = self
4870 .buffer
4871 .read(cx)
4872 .text_anchor_for_position(selection.tail(), cx)?;
4873 if tail_buffer != cursor_buffer {
4874 return None;
4875 }
4876
4877 let snapshot = cursor_buffer.read(cx).snapshot();
4878 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
4879 let prepare_rename = project.update(cx, |project, cx| {
4880 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
4881 });
4882
4883 Some(cx.spawn(|this, mut cx| async move {
4884 let rename_range = if let Some(range) = prepare_rename.await? {
4885 Some(range)
4886 } else {
4887 this.read_with(&cx, |this, cx| {
4888 let buffer = this.buffer.read(cx).snapshot(cx);
4889 let mut buffer_highlights = this
4890 .document_highlights_for_position(selection.head(), &buffer)
4891 .filter(|highlight| {
4892 highlight.start.excerpt_id() == selection.head().excerpt_id()
4893 && highlight.end.excerpt_id() == selection.head().excerpt_id()
4894 });
4895 buffer_highlights
4896 .next()
4897 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
4898 })
4899 };
4900 if let Some(rename_range) = rename_range {
4901 let rename_buffer_range = rename_range.to_offset(&snapshot);
4902 let cursor_offset_in_rename_range =
4903 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
4904
4905 this.update(&mut cx, |this, cx| {
4906 this.take_rename(false, cx);
4907 let style = this.style(cx);
4908 let buffer = this.buffer.read(cx).read(cx);
4909 let cursor_offset = selection.head().to_offset(&buffer);
4910 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
4911 let rename_end = rename_start + rename_buffer_range.len();
4912 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
4913 let mut old_highlight_id = None;
4914 let old_name: Arc<str> = buffer
4915 .chunks(rename_start..rename_end, true)
4916 .map(|chunk| {
4917 if old_highlight_id.is_none() {
4918 old_highlight_id = chunk.syntax_highlight_id;
4919 }
4920 chunk.text
4921 })
4922 .collect::<String>()
4923 .into();
4924
4925 drop(buffer);
4926
4927 // Position the selection in the rename editor so that it matches the current selection.
4928 this.show_local_selections = false;
4929 let rename_editor = cx.add_view(|cx| {
4930 let mut editor = Editor::single_line(None, cx);
4931 if let Some(old_highlight_id) = old_highlight_id {
4932 editor.override_text_style =
4933 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
4934 }
4935 editor.buffer.update(cx, |buffer, cx| {
4936 buffer.edit([(0..0, old_name.clone())], None, cx)
4937 });
4938 editor.select_all(&SelectAll, cx);
4939 editor
4940 });
4941
4942 let ranges = this
4943 .clear_background_highlights::<DocumentHighlightWrite>(cx)
4944 .into_iter()
4945 .flat_map(|(_, ranges)| ranges)
4946 .chain(
4947 this.clear_background_highlights::<DocumentHighlightRead>(cx)
4948 .into_iter()
4949 .flat_map(|(_, ranges)| ranges),
4950 )
4951 .collect();
4952
4953 this.highlight_text::<Rename>(
4954 ranges,
4955 HighlightStyle {
4956 fade_out: Some(style.rename_fade),
4957 ..Default::default()
4958 },
4959 cx,
4960 );
4961 cx.focus(&rename_editor);
4962 let block_id = this.insert_blocks(
4963 [BlockProperties {
4964 style: BlockStyle::Flex,
4965 position: range.start.clone(),
4966 height: 1,
4967 render: Arc::new({
4968 let editor = rename_editor.clone();
4969 move |cx: &mut BlockContext| {
4970 ChildView::new(editor.clone())
4971 .contained()
4972 .with_padding_left(cx.anchor_x)
4973 .boxed()
4974 }
4975 }),
4976 disposition: BlockDisposition::Below,
4977 }],
4978 cx,
4979 )[0];
4980 this.pending_rename = Some(RenameState {
4981 range,
4982 old_name,
4983 editor: rename_editor,
4984 block_id,
4985 });
4986 });
4987 }
4988
4989 Ok(())
4990 }))
4991 }
4992
4993 pub fn confirm_rename(
4994 workspace: &mut Workspace,
4995 _: &ConfirmRename,
4996 cx: &mut ViewContext<Workspace>,
4997 ) -> Option<Task<Result<()>>> {
4998 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
4999
5000 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
5001 let rename = editor.take_rename(false, cx)?;
5002 let buffer = editor.buffer.read(cx);
5003 let (start_buffer, start) =
5004 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
5005 let (end_buffer, end) =
5006 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
5007 if start_buffer == end_buffer {
5008 let new_name = rename.editor.read(cx).text(cx);
5009 Some((start_buffer, start..end, rename.old_name, new_name))
5010 } else {
5011 None
5012 }
5013 })?;
5014
5015 let rename = workspace.project().clone().update(cx, |project, cx| {
5016 project.perform_rename(
5017 buffer.clone(),
5018 range.start.clone(),
5019 new_name.clone(),
5020 true,
5021 cx,
5022 )
5023 });
5024
5025 Some(cx.spawn(|workspace, mut cx| async move {
5026 let project_transaction = rename.await?;
5027 Self::open_project_transaction(
5028 editor.clone(),
5029 workspace,
5030 project_transaction,
5031 format!("Rename: {} → {}", old_name, new_name),
5032 cx.clone(),
5033 )
5034 .await?;
5035
5036 editor.update(&mut cx, |editor, cx| {
5037 editor.refresh_document_highlights(cx);
5038 });
5039 Ok(())
5040 }))
5041 }
5042
5043 fn take_rename(
5044 &mut self,
5045 moving_cursor: bool,
5046 cx: &mut ViewContext<Self>,
5047 ) -> Option<RenameState> {
5048 let rename = self.pending_rename.take()?;
5049 self.remove_blocks([rename.block_id].into_iter().collect(), cx);
5050 self.clear_text_highlights::<Rename>(cx);
5051 self.show_local_selections = true;
5052
5053 if moving_cursor {
5054 let rename_editor = rename.editor.read(cx);
5055 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
5056
5057 // Update the selection to match the position of the selection inside
5058 // the rename editor.
5059 let snapshot = self.buffer.read(cx).read(cx);
5060 let rename_range = rename.range.to_offset(&snapshot);
5061 let cursor_in_editor = snapshot
5062 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
5063 .min(rename_range.end);
5064 drop(snapshot);
5065
5066 self.change_selections(None, cx, |s| {
5067 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
5068 });
5069 } else {
5070 self.refresh_document_highlights(cx);
5071 }
5072
5073 Some(rename)
5074 }
5075
5076 #[cfg(any(test, feature = "test-support"))]
5077 pub fn pending_rename(&self) -> Option<&RenameState> {
5078 self.pending_rename.as_ref()
5079 }
5080
5081 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
5082 if let Some(project) = self.project.clone() {
5083 self.buffer.update(cx, |multi_buffer, cx| {
5084 project.update(cx, |project, cx| {
5085 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
5086 });
5087 })
5088 }
5089 }
5090
5091 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
5092 cx.show_character_palette();
5093 }
5094
5095 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
5096 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
5097 let buffer = self.buffer.read(cx).snapshot(cx);
5098 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
5099 let is_valid = buffer
5100 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
5101 .any(|entry| {
5102 entry.diagnostic.is_primary
5103 && !entry.range.is_empty()
5104 && entry.range.start == primary_range_start
5105 && entry.diagnostic.message == active_diagnostics.primary_message
5106 });
5107
5108 if is_valid != active_diagnostics.is_valid {
5109 active_diagnostics.is_valid = is_valid;
5110 let mut new_styles = HashMap::default();
5111 for (block_id, diagnostic) in &active_diagnostics.blocks {
5112 new_styles.insert(
5113 *block_id,
5114 diagnostic_block_renderer(diagnostic.clone(), is_valid),
5115 );
5116 }
5117 self.display_map
5118 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
5119 }
5120 }
5121 }
5122
5123 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) {
5124 self.dismiss_diagnostics(cx);
5125 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
5126 let buffer = self.buffer.read(cx).snapshot(cx);
5127
5128 let mut primary_range = None;
5129 let mut primary_message = None;
5130 let mut group_end = Point::zero();
5131 let diagnostic_group = buffer
5132 .diagnostic_group::<Point>(group_id)
5133 .map(|entry| {
5134 if entry.range.end > group_end {
5135 group_end = entry.range.end;
5136 }
5137 if entry.diagnostic.is_primary {
5138 primary_range = Some(entry.range.clone());
5139 primary_message = Some(entry.diagnostic.message.clone());
5140 }
5141 entry
5142 })
5143 .collect::<Vec<_>>();
5144 let primary_range = primary_range.unwrap();
5145 let primary_message = primary_message.unwrap();
5146 let primary_range =
5147 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
5148
5149 let blocks = display_map
5150 .insert_blocks(
5151 diagnostic_group.iter().map(|entry| {
5152 let diagnostic = entry.diagnostic.clone();
5153 let message_height = diagnostic.message.lines().count() as u8;
5154 BlockProperties {
5155 style: BlockStyle::Fixed,
5156 position: buffer.anchor_after(entry.range.start),
5157 height: message_height,
5158 render: diagnostic_block_renderer(diagnostic, true),
5159 disposition: BlockDisposition::Below,
5160 }
5161 }),
5162 cx,
5163 )
5164 .into_iter()
5165 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
5166 .collect();
5167
5168 Some(ActiveDiagnosticGroup {
5169 primary_range,
5170 primary_message,
5171 blocks,
5172 is_valid: true,
5173 })
5174 });
5175 }
5176
5177 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
5178 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
5179 self.display_map.update(cx, |display_map, cx| {
5180 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
5181 });
5182 cx.notify();
5183 }
5184 }
5185
5186 pub fn set_selections_from_remote(
5187 &mut self,
5188 selections: Vec<Selection<Anchor>>,
5189 cx: &mut ViewContext<Self>,
5190 ) {
5191 let old_cursor_position = self.selections.newest_anchor().head();
5192 self.selections.change_with(cx, |s| {
5193 s.select_anchors(selections);
5194 });
5195 self.selections_did_change(false, &old_cursor_position, cx);
5196 }
5197
5198 fn push_to_selection_history(&mut self) {
5199 self.selection_history.push(SelectionHistoryEntry {
5200 selections: self.selections.disjoint_anchors().clone(),
5201 select_next_state: self.select_next_state.clone(),
5202 add_selections_state: self.add_selections_state.clone(),
5203 });
5204 }
5205
5206 pub fn request_autoscroll(&mut self, autoscroll: Autoscroll, cx: &mut ViewContext<Self>) {
5207 self.autoscroll_request = Some((autoscroll, true));
5208 cx.notify();
5209 }
5210
5211 fn request_autoscroll_remotely(&mut self, autoscroll: Autoscroll, cx: &mut ViewContext<Self>) {
5212 self.autoscroll_request = Some((autoscroll, false));
5213 cx.notify();
5214 }
5215
5216 pub fn transact(
5217 &mut self,
5218 cx: &mut ViewContext<Self>,
5219 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
5220 ) -> Option<TransactionId> {
5221 self.start_transaction_at(Instant::now(), cx);
5222 update(self, cx);
5223 self.end_transaction_at(Instant::now(), cx)
5224 }
5225
5226 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
5227 self.end_selection(cx);
5228 if let Some(tx_id) = self
5229 .buffer
5230 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
5231 {
5232 self.selection_history
5233 .insert_transaction(tx_id, self.selections.disjoint_anchors().clone());
5234 }
5235 }
5236
5237 fn end_transaction_at(
5238 &mut self,
5239 now: Instant,
5240 cx: &mut ViewContext<Self>,
5241 ) -> Option<TransactionId> {
5242 if let Some(tx_id) = self
5243 .buffer
5244 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
5245 {
5246 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
5247 *end_selections = Some(self.selections.disjoint_anchors().clone());
5248 } else {
5249 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
5250 }
5251
5252 cx.emit(Event::Edited);
5253 Some(tx_id)
5254 } else {
5255 None
5256 }
5257 }
5258
5259 pub fn page_up(&mut self, _: &PageUp, _: &mut ViewContext<Self>) {
5260 log::info!("Editor::page_up");
5261 }
5262
5263 pub fn page_down(&mut self, _: &PageDown, _: &mut ViewContext<Self>) {
5264 log::info!("Editor::page_down");
5265 }
5266
5267 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
5268 let mut fold_ranges = Vec::new();
5269
5270 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5271 let selections = self.selections.all::<Point>(cx);
5272 for selection in selections {
5273 let range = selection.display_range(&display_map).sorted();
5274 let buffer_start_row = range.start.to_point(&display_map).row;
5275
5276 for row in (0..=range.end.row()).rev() {
5277 if self.is_line_foldable(&display_map, row) && !display_map.is_line_folded(row) {
5278 let fold_range = self.foldable_range_for_line(&display_map, row);
5279 if fold_range.end.row >= buffer_start_row {
5280 fold_ranges.push(fold_range);
5281 if row <= range.start.row() {
5282 break;
5283 }
5284 }
5285 }
5286 }
5287 }
5288
5289 self.fold_ranges(fold_ranges, cx);
5290 }
5291
5292 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
5293 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5294 let buffer = &display_map.buffer_snapshot;
5295 let selections = self.selections.all::<Point>(cx);
5296 let ranges = selections
5297 .iter()
5298 .map(|s| {
5299 let range = s.display_range(&display_map).sorted();
5300 let mut start = range.start.to_point(&display_map);
5301 let mut end = range.end.to_point(&display_map);
5302 start.column = 0;
5303 end.column = buffer.line_len(end.row);
5304 start..end
5305 })
5306 .collect::<Vec<_>>();
5307 self.unfold_ranges(ranges, true, cx);
5308 }
5309
5310 fn is_line_foldable(&self, display_map: &DisplaySnapshot, display_row: u32) -> bool {
5311 let max_point = display_map.max_point();
5312 if display_row >= max_point.row() {
5313 false
5314 } else {
5315 let (start_indent, is_blank) = display_map.line_indent(display_row);
5316 if is_blank {
5317 false
5318 } else {
5319 for display_row in display_row + 1..=max_point.row() {
5320 let (indent, is_blank) = display_map.line_indent(display_row);
5321 if !is_blank {
5322 return indent > start_indent;
5323 }
5324 }
5325 false
5326 }
5327 }
5328 }
5329
5330 fn foldable_range_for_line(
5331 &self,
5332 display_map: &DisplaySnapshot,
5333 start_row: u32,
5334 ) -> Range<Point> {
5335 let max_point = display_map.max_point();
5336
5337 let (start_indent, _) = display_map.line_indent(start_row);
5338 let start = DisplayPoint::new(start_row, display_map.line_len(start_row));
5339 let mut end = None;
5340 for row in start_row + 1..=max_point.row() {
5341 let (indent, is_blank) = display_map.line_indent(row);
5342 if !is_blank && indent <= start_indent {
5343 end = Some(DisplayPoint::new(row - 1, display_map.line_len(row - 1)));
5344 break;
5345 }
5346 }
5347
5348 let end = end.unwrap_or(max_point);
5349 return start.to_point(display_map)..end.to_point(display_map);
5350 }
5351
5352 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
5353 let selections = self.selections.all::<Point>(cx);
5354 let ranges = selections.into_iter().map(|s| s.start..s.end);
5355 self.fold_ranges(ranges, cx);
5356 }
5357
5358 pub fn fold_ranges<T: ToOffset>(
5359 &mut self,
5360 ranges: impl IntoIterator<Item = Range<T>>,
5361 cx: &mut ViewContext<Self>,
5362 ) {
5363 let mut ranges = ranges.into_iter().peekable();
5364 if ranges.peek().is_some() {
5365 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
5366 self.request_autoscroll(Autoscroll::Fit, cx);
5367 cx.notify();
5368 }
5369 }
5370
5371 pub fn unfold_ranges<T: ToOffset>(
5372 &mut self,
5373 ranges: impl IntoIterator<Item = Range<T>>,
5374 inclusive: bool,
5375 cx: &mut ViewContext<Self>,
5376 ) {
5377 let mut ranges = ranges.into_iter().peekable();
5378 if ranges.peek().is_some() {
5379 self.display_map
5380 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
5381 self.request_autoscroll(Autoscroll::Fit, cx);
5382 cx.notify();
5383 }
5384 }
5385
5386 pub fn insert_blocks(
5387 &mut self,
5388 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
5389 cx: &mut ViewContext<Self>,
5390 ) -> Vec<BlockId> {
5391 let blocks = self
5392 .display_map
5393 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
5394 self.request_autoscroll(Autoscroll::Fit, cx);
5395 blocks
5396 }
5397
5398 pub fn replace_blocks(
5399 &mut self,
5400 blocks: HashMap<BlockId, RenderBlock>,
5401 cx: &mut ViewContext<Self>,
5402 ) {
5403 self.display_map
5404 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
5405 self.request_autoscroll(Autoscroll::Fit, cx);
5406 }
5407
5408 pub fn remove_blocks(&mut self, block_ids: HashSet<BlockId>, cx: &mut ViewContext<Self>) {
5409 self.display_map.update(cx, |display_map, cx| {
5410 display_map.remove_blocks(block_ids, cx)
5411 });
5412 }
5413
5414 pub fn longest_row(&self, cx: &mut MutableAppContext) -> u32 {
5415 self.display_map
5416 .update(cx, |map, cx| map.snapshot(cx))
5417 .longest_row()
5418 }
5419
5420 pub fn max_point(&self, cx: &mut MutableAppContext) -> DisplayPoint {
5421 self.display_map
5422 .update(cx, |map, cx| map.snapshot(cx))
5423 .max_point()
5424 }
5425
5426 pub fn text(&self, cx: &AppContext) -> String {
5427 self.buffer.read(cx).read(cx).text()
5428 }
5429
5430 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
5431 self.transact(cx, |this, cx| {
5432 this.buffer
5433 .read(cx)
5434 .as_singleton()
5435 .expect("you can only call set_text on editors for singleton buffers")
5436 .update(cx, |buffer, cx| buffer.set_text(text, cx));
5437 });
5438 }
5439
5440 pub fn display_text(&self, cx: &mut MutableAppContext) -> String {
5441 self.display_map
5442 .update(cx, |map, cx| map.snapshot(cx))
5443 .text()
5444 }
5445
5446 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
5447 let language_name = self
5448 .buffer
5449 .read(cx)
5450 .as_singleton()
5451 .and_then(|singleton_buffer| singleton_buffer.read(cx).language())
5452 .map(|l| l.name());
5453
5454 let settings = cx.global::<Settings>();
5455 let mode = self
5456 .soft_wrap_mode_override
5457 .unwrap_or_else(|| settings.soft_wrap(language_name.as_deref()));
5458 match mode {
5459 settings::SoftWrap::None => SoftWrap::None,
5460 settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
5461 settings::SoftWrap::PreferredLineLength => {
5462 SoftWrap::Column(settings.preferred_line_length(language_name.as_deref()))
5463 }
5464 }
5465 }
5466
5467 pub fn set_soft_wrap_mode(&mut self, mode: settings::SoftWrap, cx: &mut ViewContext<Self>) {
5468 self.soft_wrap_mode_override = Some(mode);
5469 cx.notify();
5470 }
5471
5472 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut MutableAppContext) -> bool {
5473 self.display_map
5474 .update(cx, |map, cx| map.set_wrap_width(width, cx))
5475 }
5476
5477 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
5478 self.highlighted_rows = rows;
5479 }
5480
5481 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
5482 self.highlighted_rows.clone()
5483 }
5484
5485 pub fn highlight_background<T: 'static>(
5486 &mut self,
5487 ranges: Vec<Range<Anchor>>,
5488 color_fetcher: fn(&Theme) -> Color,
5489 cx: &mut ViewContext<Self>,
5490 ) {
5491 self.background_highlights
5492 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
5493 cx.notify();
5494 }
5495
5496 pub fn clear_background_highlights<T: 'static>(
5497 &mut self,
5498 cx: &mut ViewContext<Self>,
5499 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
5500 cx.notify();
5501 self.background_highlights.remove(&TypeId::of::<T>())
5502 }
5503
5504 #[cfg(feature = "test-support")]
5505 pub fn all_background_highlights(
5506 &mut self,
5507 cx: &mut ViewContext<Self>,
5508 ) -> Vec<(Range<DisplayPoint>, Color)> {
5509 let snapshot = self.snapshot(cx);
5510 let buffer = &snapshot.buffer_snapshot;
5511 let start = buffer.anchor_before(0);
5512 let end = buffer.anchor_after(buffer.len());
5513 let theme = cx.global::<Settings>().theme.as_ref();
5514 self.background_highlights_in_range(start..end, &snapshot, theme)
5515 }
5516
5517 fn document_highlights_for_position<'a>(
5518 &'a self,
5519 position: Anchor,
5520 buffer: &'a MultiBufferSnapshot,
5521 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
5522 let read_highlights = self
5523 .background_highlights
5524 .get(&TypeId::of::<DocumentHighlightRead>())
5525 .map(|h| &h.1);
5526 let write_highlights = self
5527 .background_highlights
5528 .get(&TypeId::of::<DocumentHighlightWrite>())
5529 .map(|h| &h.1);
5530 let left_position = position.bias_left(buffer);
5531 let right_position = position.bias_right(buffer);
5532 read_highlights
5533 .into_iter()
5534 .chain(write_highlights)
5535 .flat_map(move |ranges| {
5536 let start_ix = match ranges.binary_search_by(|probe| {
5537 let cmp = probe.end.cmp(&left_position, &buffer);
5538 if cmp.is_ge() {
5539 Ordering::Greater
5540 } else {
5541 Ordering::Less
5542 }
5543 }) {
5544 Ok(i) | Err(i) => i,
5545 };
5546
5547 let right_position = right_position.clone();
5548 ranges[start_ix..]
5549 .iter()
5550 .take_while(move |range| range.start.cmp(&right_position, &buffer).is_le())
5551 })
5552 }
5553
5554 pub fn background_highlights_in_range(
5555 &self,
5556 search_range: Range<Anchor>,
5557 display_snapshot: &DisplaySnapshot,
5558 theme: &Theme,
5559 ) -> Vec<(Range<DisplayPoint>, Color)> {
5560 let mut results = Vec::new();
5561 let buffer = &display_snapshot.buffer_snapshot;
5562 for (color_fetcher, ranges) in self.background_highlights.values() {
5563 let color = color_fetcher(theme);
5564 let start_ix = match ranges.binary_search_by(|probe| {
5565 let cmp = probe.end.cmp(&search_range.start, &buffer);
5566 if cmp.is_gt() {
5567 Ordering::Greater
5568 } else {
5569 Ordering::Less
5570 }
5571 }) {
5572 Ok(i) | Err(i) => i,
5573 };
5574 for range in &ranges[start_ix..] {
5575 if range.start.cmp(&search_range.end, &buffer).is_ge() {
5576 break;
5577 }
5578 let start = range
5579 .start
5580 .to_point(buffer)
5581 .to_display_point(display_snapshot);
5582 let end = range
5583 .end
5584 .to_point(buffer)
5585 .to_display_point(display_snapshot);
5586 results.push((start..end, color))
5587 }
5588 }
5589 results
5590 }
5591
5592 pub fn highlight_text<T: 'static>(
5593 &mut self,
5594 ranges: Vec<Range<Anchor>>,
5595 style: HighlightStyle,
5596 cx: &mut ViewContext<Self>,
5597 ) {
5598 self.display_map.update(cx, |map, _| {
5599 map.highlight_text(TypeId::of::<T>(), ranges, style)
5600 });
5601 cx.notify();
5602 }
5603
5604 pub fn text_highlights<'a, T: 'static>(
5605 &'a self,
5606 cx: &'a AppContext,
5607 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
5608 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
5609 }
5610
5611 pub fn clear_text_highlights<T: 'static>(
5612 &mut self,
5613 cx: &mut ViewContext<Self>,
5614 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
5615 cx.notify();
5616 self.display_map
5617 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()))
5618 }
5619
5620 fn next_blink_epoch(&mut self) -> usize {
5621 self.blink_epoch += 1;
5622 self.blink_epoch
5623 }
5624
5625 fn pause_cursor_blinking(&mut self, cx: &mut ViewContext<Self>) {
5626 if !self.focused {
5627 return;
5628 }
5629
5630 self.show_local_cursors = true;
5631 cx.notify();
5632
5633 let epoch = self.next_blink_epoch();
5634 cx.spawn(|this, mut cx| {
5635 let this = this.downgrade();
5636 async move {
5637 Timer::after(CURSOR_BLINK_INTERVAL).await;
5638 if let Some(this) = this.upgrade(&cx) {
5639 this.update(&mut cx, |this, cx| this.resume_cursor_blinking(epoch, cx))
5640 }
5641 }
5642 })
5643 .detach();
5644 }
5645
5646 fn resume_cursor_blinking(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
5647 if epoch == self.blink_epoch {
5648 self.blinking_paused = false;
5649 self.blink_cursors(epoch, cx);
5650 }
5651 }
5652
5653 fn blink_cursors(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
5654 if epoch == self.blink_epoch && self.focused && !self.blinking_paused {
5655 self.show_local_cursors = !self.show_local_cursors;
5656 cx.notify();
5657
5658 let epoch = self.next_blink_epoch();
5659 cx.spawn(|this, mut cx| {
5660 let this = this.downgrade();
5661 async move {
5662 Timer::after(CURSOR_BLINK_INTERVAL).await;
5663 if let Some(this) = this.upgrade(&cx) {
5664 this.update(&mut cx, |this, cx| this.blink_cursors(epoch, cx));
5665 }
5666 }
5667 })
5668 .detach();
5669 }
5670 }
5671
5672 pub fn show_local_cursors(&self) -> bool {
5673 self.show_local_cursors && self.focused
5674 }
5675
5676 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
5677 cx.notify();
5678 }
5679
5680 fn on_buffer_event(
5681 &mut self,
5682 _: ModelHandle<MultiBuffer>,
5683 event: &language::Event,
5684 cx: &mut ViewContext<Self>,
5685 ) {
5686 match event {
5687 language::Event::Edited => {
5688 self.refresh_active_diagnostics(cx);
5689 self.refresh_code_actions(cx);
5690 cx.emit(Event::BufferEdited);
5691 }
5692 language::Event::Reparsed => cx.emit(Event::Reparsed),
5693 language::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
5694 language::Event::Saved => cx.emit(Event::Saved),
5695 language::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
5696 language::Event::Reloaded => cx.emit(Event::TitleChanged),
5697 language::Event::Closed => cx.emit(Event::Closed),
5698 language::Event::DiagnosticsUpdated => {
5699 self.refresh_active_diagnostics(cx);
5700 }
5701 _ => {}
5702 }
5703 }
5704
5705 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
5706 cx.notify();
5707 }
5708
5709 pub fn set_searchable(&mut self, searchable: bool) {
5710 self.searchable = searchable;
5711 }
5712
5713 pub fn searchable(&self) -> bool {
5714 self.searchable
5715 }
5716
5717 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
5718 let active_item = workspace.active_item(cx);
5719 let editor_handle = if let Some(editor) = active_item
5720 .as_ref()
5721 .and_then(|item| item.act_as::<Self>(cx))
5722 {
5723 editor
5724 } else {
5725 cx.propagate_action();
5726 return;
5727 };
5728
5729 let editor = editor_handle.read(cx);
5730 let buffer = editor.buffer.read(cx);
5731 if buffer.is_singleton() {
5732 cx.propagate_action();
5733 return;
5734 }
5735
5736 let mut new_selections_by_buffer = HashMap::default();
5737 for selection in editor.selections.all::<usize>(cx) {
5738 for (buffer, mut range) in
5739 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
5740 {
5741 if selection.reversed {
5742 mem::swap(&mut range.start, &mut range.end);
5743 }
5744 new_selections_by_buffer
5745 .entry(buffer)
5746 .or_insert(Vec::new())
5747 .push(range)
5748 }
5749 }
5750
5751 editor_handle.update(cx, |editor, cx| {
5752 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
5753 });
5754 let pane = workspace.active_pane().clone();
5755 pane.update(cx, |pane, _| pane.disable_history());
5756
5757 // We defer the pane interaction because we ourselves are a workspace item
5758 // and activating a new item causes the pane to call a method on us reentrantly,
5759 // which panics if we're on the stack.
5760 cx.defer(move |workspace, cx| {
5761 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
5762 let editor = workspace.open_project_item::<Self>(buffer, cx);
5763 editor.update(cx, |editor, cx| {
5764 editor.change_selections(Some(Autoscroll::Newest), cx, |s| {
5765 s.select_ranges(ranges);
5766 });
5767 });
5768 }
5769
5770 pane.update(cx, |pane, _| pane.enable_history());
5771 });
5772 }
5773
5774 fn jump(workspace: &mut Workspace, action: &Jump, cx: &mut ViewContext<Workspace>) {
5775 let editor = workspace.open_path(action.path.clone(), true, cx);
5776 let position = action.position;
5777 let anchor = action.anchor;
5778 cx.spawn_weak(|_, mut cx| async move {
5779 let editor = editor.await.log_err()?.downcast::<Editor>()?;
5780 editor.update(&mut cx, |editor, cx| {
5781 let buffer = editor.buffer().read(cx).as_singleton()?;
5782 let buffer = buffer.read(cx);
5783 let cursor = if buffer.can_resolve(&anchor) {
5784 language::ToPoint::to_point(&anchor, buffer)
5785 } else {
5786 buffer.clip_point(position, Bias::Left)
5787 };
5788
5789 let nav_history = editor.nav_history.take();
5790 editor.change_selections(Some(Autoscroll::Newest), cx, |s| {
5791 s.select_ranges([cursor..cursor]);
5792 });
5793 editor.nav_history = nav_history;
5794
5795 Some(())
5796 })?;
5797 Some(())
5798 })
5799 .detach()
5800 }
5801
5802 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
5803 let snapshot = self.buffer.read(cx).read(cx);
5804 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
5805 Some(
5806 ranges
5807 .into_iter()
5808 .map(move |range| {
5809 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
5810 })
5811 .collect(),
5812 )
5813 }
5814
5815 fn selection_replacement_ranges(
5816 &self,
5817 range: Range<OffsetUtf16>,
5818 cx: &AppContext,
5819 ) -> Vec<Range<OffsetUtf16>> {
5820 let selections = self.selections.all::<OffsetUtf16>(cx);
5821 let newest_selection = selections
5822 .iter()
5823 .max_by_key(|selection| selection.id)
5824 .unwrap();
5825 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
5826 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
5827 let snapshot = self.buffer.read(cx).read(cx);
5828 selections
5829 .into_iter()
5830 .map(|mut selection| {
5831 selection.start.0 =
5832 (selection.start.0 as isize).saturating_add(start_delta) as usize;
5833 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
5834 snapshot.clip_offset_utf16(selection.start, Bias::Left)
5835 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
5836 })
5837 .collect()
5838 }
5839}
5840
5841impl EditorSnapshot {
5842 pub fn is_focused(&self) -> bool {
5843 self.is_focused
5844 }
5845
5846 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
5847 self.placeholder_text.as_ref()
5848 }
5849
5850 pub fn scroll_position(&self) -> Vector2F {
5851 compute_scroll_position(
5852 &self.display_snapshot,
5853 self.scroll_position,
5854 &self.scroll_top_anchor,
5855 )
5856 }
5857}
5858
5859impl Deref for EditorSnapshot {
5860 type Target = DisplaySnapshot;
5861
5862 fn deref(&self) -> &Self::Target {
5863 &self.display_snapshot
5864 }
5865}
5866
5867fn compute_scroll_position(
5868 snapshot: &DisplaySnapshot,
5869 mut scroll_position: Vector2F,
5870 scroll_top_anchor: &Anchor,
5871) -> Vector2F {
5872 if *scroll_top_anchor != Anchor::min() {
5873 let scroll_top = scroll_top_anchor.to_display_point(snapshot).row() as f32;
5874 scroll_position.set_y(scroll_top + scroll_position.y());
5875 } else {
5876 scroll_position.set_y(0.);
5877 }
5878 scroll_position
5879}
5880
5881#[derive(Copy, Clone, Debug, PartialEq, Eq)]
5882pub enum Event {
5883 Activate,
5884 BufferEdited,
5885 Edited,
5886 Reparsed,
5887 Blurred,
5888 DirtyChanged,
5889 Saved,
5890 TitleChanged,
5891 SelectionsChanged { local: bool },
5892 ScrollPositionChanged { local: bool },
5893 Closed,
5894 IgnoredInput,
5895}
5896
5897pub struct EditorFocused(pub ViewHandle<Editor>);
5898pub struct EditorBlurred(pub ViewHandle<Editor>);
5899pub struct EditorReleased(pub WeakViewHandle<Editor>);
5900
5901impl Entity for Editor {
5902 type Event = Event;
5903
5904 fn release(&mut self, cx: &mut MutableAppContext) {
5905 cx.emit_global(EditorReleased(self.handle.clone()));
5906 }
5907}
5908
5909impl View for Editor {
5910 fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
5911 let style = self.style(cx);
5912 let font_changed = self.display_map.update(cx, |map, cx| {
5913 map.set_font(style.text.font_id, style.text.font_size, cx)
5914 });
5915
5916 if font_changed {
5917 let handle = self.handle.clone();
5918 cx.defer(move |cx| {
5919 if let Some(editor) = handle.upgrade(cx) {
5920 editor.update(cx, |editor, cx| {
5921 hide_hover(editor, cx);
5922 })
5923 }
5924 });
5925 }
5926
5927 Stack::new()
5928 .with_child(
5929 EditorElement::new(self.handle.clone(), style.clone(), self.cursor_shape).boxed(),
5930 )
5931 .with_child(ChildView::new(&self.mouse_context_menu).boxed())
5932 .boxed()
5933 }
5934
5935 fn ui_name() -> &'static str {
5936 "Editor"
5937 }
5938
5939 fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
5940 let focused_event = EditorFocused(cx.handle());
5941 cx.emit_global(focused_event);
5942 if let Some(rename) = self.pending_rename.as_ref() {
5943 cx.focus(&rename.editor);
5944 } else {
5945 self.focused = true;
5946 self.blink_cursors(self.blink_epoch, cx);
5947 self.buffer.update(cx, |buffer, cx| {
5948 buffer.finalize_last_transaction(cx);
5949 if self.leader_replica_id.is_none() {
5950 buffer.set_active_selections(
5951 &self.selections.disjoint_anchors(),
5952 self.selections.line_mode,
5953 cx,
5954 );
5955 }
5956 });
5957 }
5958 }
5959
5960 fn on_blur(&mut self, cx: &mut ViewContext<Self>) {
5961 let blurred_event = EditorBlurred(cx.handle());
5962 cx.emit_global(blurred_event);
5963 self.focused = false;
5964 self.buffer
5965 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
5966 self.hide_context_menu(cx);
5967 hide_hover(self, cx);
5968 cx.emit(Event::Blurred);
5969 cx.notify();
5970 }
5971
5972 fn keymap_context(&self, _: &AppContext) -> gpui::keymap::Context {
5973 let mut context = Self::default_keymap_context();
5974 let mode = match self.mode {
5975 EditorMode::SingleLine => "single_line",
5976 EditorMode::AutoHeight { .. } => "auto_height",
5977 EditorMode::Full => "full",
5978 };
5979 context.map.insert("mode".into(), mode.into());
5980 if self.pending_rename.is_some() {
5981 context.set.insert("renaming".into());
5982 }
5983 match self.context_menu.as_ref() {
5984 Some(ContextMenu::Completions(_)) => {
5985 context.set.insert("showing_completions".into());
5986 }
5987 Some(ContextMenu::CodeActions(_)) => {
5988 context.set.insert("showing_code_actions".into());
5989 }
5990 None => {}
5991 }
5992
5993 for layer in self.keymap_context_layers.values() {
5994 context.extend(layer);
5995 }
5996
5997 context
5998 }
5999
6000 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
6001 Some(
6002 self.buffer
6003 .read(cx)
6004 .read(cx)
6005 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
6006 .collect(),
6007 )
6008 }
6009
6010 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
6011 // Prevent the IME menu from appearing when holding down an alphabetic key
6012 // while input is disabled.
6013 if !self.input_enabled {
6014 return None;
6015 }
6016
6017 let range = self.selections.newest::<OffsetUtf16>(cx).range();
6018 Some(range.start.0..range.end.0)
6019 }
6020
6021 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
6022 let snapshot = self.buffer.read(cx).read(cx);
6023 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
6024 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
6025 }
6026
6027 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
6028 self.clear_text_highlights::<InputComposition>(cx);
6029 self.ime_transaction.take();
6030 }
6031
6032 fn replace_text_in_range(
6033 &mut self,
6034 range_utf16: Option<Range<usize>>,
6035 text: &str,
6036 cx: &mut ViewContext<Self>,
6037 ) {
6038 if !self.input_enabled {
6039 cx.emit(Event::IgnoredInput);
6040 return;
6041 }
6042
6043 self.transact(cx, |this, cx| {
6044 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
6045 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
6046 Some(this.selection_replacement_ranges(range_utf16, cx))
6047 } else if let Some(marked_ranges) = this.marked_text_ranges(cx) {
6048 Some(marked_ranges)
6049 } else {
6050 None
6051 };
6052
6053 if let Some(new_selected_ranges) = new_selected_ranges {
6054 this.change_selections(None, cx, |selections| {
6055 selections.select_ranges(new_selected_ranges)
6056 });
6057 }
6058 this.handle_input(text, cx);
6059 });
6060
6061 if let Some(transaction) = self.ime_transaction {
6062 self.buffer.update(cx, |buffer, cx| {
6063 buffer.group_until_transaction(transaction, cx);
6064 });
6065 }
6066
6067 self.unmark_text(cx);
6068 }
6069
6070 fn replace_and_mark_text_in_range(
6071 &mut self,
6072 range_utf16: Option<Range<usize>>,
6073 text: &str,
6074 new_selected_range_utf16: Option<Range<usize>>,
6075 cx: &mut ViewContext<Self>,
6076 ) {
6077 if !self.input_enabled {
6078 cx.emit(Event::IgnoredInput);
6079 return;
6080 }
6081
6082 let transaction = self.transact(cx, |this, cx| {
6083 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
6084 let snapshot = this.buffer.read(cx).read(cx);
6085 if let Some(relative_range_utf16) = range_utf16.as_ref() {
6086 for marked_range in &mut marked_ranges {
6087 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
6088 marked_range.start.0 += relative_range_utf16.start;
6089 marked_range.start =
6090 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
6091 marked_range.end =
6092 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
6093 }
6094 }
6095 Some(marked_ranges)
6096 } else if let Some(range_utf16) = range_utf16 {
6097 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
6098 Some(this.selection_replacement_ranges(range_utf16, cx))
6099 } else {
6100 None
6101 };
6102
6103 if let Some(ranges) = ranges_to_replace {
6104 this.change_selections(None, cx, |s| s.select_ranges(ranges));
6105 }
6106
6107 let marked_ranges = {
6108 let snapshot = this.buffer.read(cx).read(cx);
6109 this.selections
6110 .disjoint_anchors()
6111 .into_iter()
6112 .map(|selection| {
6113 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
6114 })
6115 .collect::<Vec<_>>()
6116 };
6117
6118 if text.is_empty() {
6119 this.unmark_text(cx);
6120 } else {
6121 this.highlight_text::<InputComposition>(
6122 marked_ranges.clone(),
6123 this.style(cx).composition_mark,
6124 cx,
6125 );
6126 }
6127
6128 this.handle_input(text, cx);
6129
6130 if let Some(new_selected_range) = new_selected_range_utf16 {
6131 let snapshot = this.buffer.read(cx).read(cx);
6132 let new_selected_ranges = marked_ranges
6133 .into_iter()
6134 .map(|marked_range| {
6135 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
6136 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
6137 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
6138 snapshot.clip_offset_utf16(new_start, Bias::Left)
6139 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
6140 })
6141 .collect::<Vec<_>>();
6142
6143 drop(snapshot);
6144 this.change_selections(None, cx, |selections| {
6145 selections.select_ranges(new_selected_ranges)
6146 });
6147 }
6148 });
6149
6150 self.ime_transaction = self.ime_transaction.or(transaction);
6151 if let Some(transaction) = self.ime_transaction {
6152 self.buffer.update(cx, |buffer, cx| {
6153 buffer.group_until_transaction(transaction, cx);
6154 });
6155 }
6156
6157 if self.text_highlights::<InputComposition>(cx).is_none() {
6158 self.ime_transaction.take();
6159 }
6160 }
6161}
6162
6163fn build_style(
6164 settings: &Settings,
6165 get_field_editor_theme: Option<GetFieldEditorTheme>,
6166 override_text_style: Option<&OverrideTextStyle>,
6167 cx: &AppContext,
6168) -> EditorStyle {
6169 let font_cache = cx.font_cache();
6170
6171 let mut theme = settings.theme.editor.clone();
6172 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
6173 let field_editor_theme = get_field_editor_theme(&settings.theme);
6174 theme.text_color = field_editor_theme.text.color;
6175 theme.selection = field_editor_theme.selection;
6176 theme.background = field_editor_theme
6177 .container
6178 .background_color
6179 .unwrap_or_default();
6180 EditorStyle {
6181 text: field_editor_theme.text,
6182 placeholder_text: field_editor_theme.placeholder_text,
6183 theme,
6184 }
6185 } else {
6186 let font_family_id = settings.buffer_font_family;
6187 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
6188 let font_properties = Default::default();
6189 let font_id = font_cache
6190 .select_font(font_family_id, &font_properties)
6191 .unwrap();
6192 let font_size = settings.buffer_font_size;
6193 EditorStyle {
6194 text: TextStyle {
6195 color: settings.theme.editor.text_color,
6196 font_family_name,
6197 font_family_id,
6198 font_id,
6199 font_size,
6200 font_properties,
6201 underline: Default::default(),
6202 },
6203 placeholder_text: None,
6204 theme,
6205 }
6206 };
6207
6208 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
6209 if let Some(highlighted) = style
6210 .text
6211 .clone()
6212 .highlight(highlight_style, font_cache)
6213 .log_err()
6214 {
6215 style.text = highlighted;
6216 }
6217 }
6218
6219 style
6220}
6221
6222trait SelectionExt {
6223 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
6224 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
6225 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
6226 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
6227 -> Range<u32>;
6228}
6229
6230impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
6231 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
6232 let start = self.start.to_point(buffer);
6233 let end = self.end.to_point(buffer);
6234 if self.reversed {
6235 end..start
6236 } else {
6237 start..end
6238 }
6239 }
6240
6241 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
6242 let start = self.start.to_offset(buffer);
6243 let end = self.end.to_offset(buffer);
6244 if self.reversed {
6245 end..start
6246 } else {
6247 start..end
6248 }
6249 }
6250
6251 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
6252 let start = self
6253 .start
6254 .to_point(&map.buffer_snapshot)
6255 .to_display_point(map);
6256 let end = self
6257 .end
6258 .to_point(&map.buffer_snapshot)
6259 .to_display_point(map);
6260 if self.reversed {
6261 end..start
6262 } else {
6263 start..end
6264 }
6265 }
6266
6267 fn spanned_rows(
6268 &self,
6269 include_end_if_at_line_start: bool,
6270 map: &DisplaySnapshot,
6271 ) -> Range<u32> {
6272 let start = self.start.to_point(&map.buffer_snapshot);
6273 let mut end = self.end.to_point(&map.buffer_snapshot);
6274 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
6275 end.row -= 1;
6276 }
6277
6278 let buffer_start = map.prev_line_boundary(start).0;
6279 let buffer_end = map.next_line_boundary(end).0;
6280 buffer_start.row..buffer_end.row + 1
6281 }
6282}
6283
6284impl<T: InvalidationRegion> InvalidationStack<T> {
6285 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
6286 where
6287 S: Clone + ToOffset,
6288 {
6289 while let Some(region) = self.last() {
6290 let all_selections_inside_invalidation_ranges =
6291 if selections.len() == region.ranges().len() {
6292 selections
6293 .iter()
6294 .zip(region.ranges().iter().map(|r| r.to_offset(&buffer)))
6295 .all(|(selection, invalidation_range)| {
6296 let head = selection.head().to_offset(&buffer);
6297 invalidation_range.start <= head && invalidation_range.end >= head
6298 })
6299 } else {
6300 false
6301 };
6302
6303 if all_selections_inside_invalidation_ranges {
6304 break;
6305 } else {
6306 self.pop();
6307 }
6308 }
6309 }
6310}
6311
6312impl<T> Default for InvalidationStack<T> {
6313 fn default() -> Self {
6314 Self(Default::default())
6315 }
6316}
6317
6318impl<T> Deref for InvalidationStack<T> {
6319 type Target = Vec<T>;
6320
6321 fn deref(&self) -> &Self::Target {
6322 &self.0
6323 }
6324}
6325
6326impl<T> DerefMut for InvalidationStack<T> {
6327 fn deref_mut(&mut self) -> &mut Self::Target {
6328 &mut self.0
6329 }
6330}
6331
6332impl InvalidationRegion for BracketPairState {
6333 fn ranges(&self) -> &[Range<Anchor>] {
6334 &self.ranges
6335 }
6336}
6337
6338impl InvalidationRegion for SnippetState {
6339 fn ranges(&self) -> &[Range<Anchor>] {
6340 &self.ranges[self.active_index]
6341 }
6342}
6343
6344impl Deref for EditorStyle {
6345 type Target = theme::Editor;
6346
6347 fn deref(&self) -> &Self::Target {
6348 &self.theme
6349 }
6350}
6351
6352pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
6353 let mut highlighted_lines = Vec::new();
6354 for line in diagnostic.message.lines() {
6355 highlighted_lines.push(highlight_diagnostic_message(line));
6356 }
6357
6358 Arc::new(move |cx: &mut BlockContext| {
6359 let settings = cx.global::<Settings>();
6360 let theme = &settings.theme.editor;
6361 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
6362 let font_size = (style.text_scale_factor * settings.buffer_font_size).round();
6363 Flex::column()
6364 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
6365 Label::new(
6366 line.clone(),
6367 style.message.clone().with_font_size(font_size),
6368 )
6369 .with_highlights(highlights.clone())
6370 .contained()
6371 .with_margin_left(cx.anchor_x)
6372 .boxed()
6373 }))
6374 .aligned()
6375 .left()
6376 .boxed()
6377 })
6378}
6379
6380pub fn highlight_diagnostic_message(message: &str) -> (String, Vec<usize>) {
6381 let mut message_without_backticks = String::new();
6382 let mut prev_offset = 0;
6383 let mut inside_block = false;
6384 let mut highlights = Vec::new();
6385 for (match_ix, (offset, _)) in message
6386 .match_indices('`')
6387 .chain([(message.len(), "")])
6388 .enumerate()
6389 {
6390 message_without_backticks.push_str(&message[prev_offset..offset]);
6391 if inside_block {
6392 highlights.extend(prev_offset - match_ix..offset - match_ix);
6393 }
6394
6395 inside_block = !inside_block;
6396 prev_offset = offset + 1;
6397 }
6398
6399 (message_without_backticks, highlights)
6400}
6401
6402pub fn diagnostic_style(
6403 severity: DiagnosticSeverity,
6404 valid: bool,
6405 theme: &theme::Editor,
6406) -> DiagnosticStyle {
6407 match (severity, valid) {
6408 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
6409 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
6410 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
6411 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
6412 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
6413 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
6414 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
6415 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
6416 _ => theme.invalid_hint_diagnostic.clone(),
6417 }
6418}
6419
6420pub fn combine_syntax_and_fuzzy_match_highlights(
6421 text: &str,
6422 default_style: HighlightStyle,
6423 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
6424 match_indices: &[usize],
6425) -> Vec<(Range<usize>, HighlightStyle)> {
6426 let mut result = Vec::new();
6427 let mut match_indices = match_indices.iter().copied().peekable();
6428
6429 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
6430 {
6431 syntax_highlight.weight = None;
6432
6433 // Add highlights for any fuzzy match characters before the next
6434 // syntax highlight range.
6435 while let Some(&match_index) = match_indices.peek() {
6436 if match_index >= range.start {
6437 break;
6438 }
6439 match_indices.next();
6440 let end_index = char_ix_after(match_index, text);
6441 let mut match_style = default_style;
6442 match_style.weight = Some(fonts::Weight::BOLD);
6443 result.push((match_index..end_index, match_style));
6444 }
6445
6446 if range.start == usize::MAX {
6447 break;
6448 }
6449
6450 // Add highlights for any fuzzy match characters within the
6451 // syntax highlight range.
6452 let mut offset = range.start;
6453 while let Some(&match_index) = match_indices.peek() {
6454 if match_index >= range.end {
6455 break;
6456 }
6457
6458 match_indices.next();
6459 if match_index > offset {
6460 result.push((offset..match_index, syntax_highlight));
6461 }
6462
6463 let mut end_index = char_ix_after(match_index, text);
6464 while let Some(&next_match_index) = match_indices.peek() {
6465 if next_match_index == end_index && next_match_index < range.end {
6466 end_index = char_ix_after(next_match_index, text);
6467 match_indices.next();
6468 } else {
6469 break;
6470 }
6471 }
6472
6473 let mut match_style = syntax_highlight;
6474 match_style.weight = Some(fonts::Weight::BOLD);
6475 result.push((match_index..end_index, match_style));
6476 offset = end_index;
6477 }
6478
6479 if offset < range.end {
6480 result.push((offset..range.end, syntax_highlight));
6481 }
6482 }
6483
6484 fn char_ix_after(ix: usize, text: &str) -> usize {
6485 ix + text[ix..].chars().next().unwrap().len_utf8()
6486 }
6487
6488 result
6489}
6490
6491pub fn styled_runs_for_code_label<'a>(
6492 label: &'a CodeLabel,
6493 syntax_theme: &'a theme::SyntaxTheme,
6494) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
6495 let fade_out = HighlightStyle {
6496 fade_out: Some(0.35),
6497 ..Default::default()
6498 };
6499
6500 let mut prev_end = label.filter_range.end;
6501 label
6502 .runs
6503 .iter()
6504 .enumerate()
6505 .flat_map(move |(ix, (range, highlight_id))| {
6506 let style = if let Some(style) = highlight_id.style(syntax_theme) {
6507 style
6508 } else {
6509 return Default::default();
6510 };
6511 let mut muted_style = style.clone();
6512 muted_style.highlight(fade_out);
6513
6514 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
6515 if range.start >= label.filter_range.end {
6516 if range.start > prev_end {
6517 runs.push((prev_end..range.start, fade_out));
6518 }
6519 runs.push((range.clone(), muted_style));
6520 } else if range.end <= label.filter_range.end {
6521 runs.push((range.clone(), style));
6522 } else {
6523 runs.push((range.start..label.filter_range.end, style));
6524 runs.push((label.filter_range.end..range.end, muted_style));
6525 }
6526 prev_end = cmp::max(prev_end, range.end);
6527
6528 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
6529 runs.push((prev_end..label.text.len(), fade_out));
6530 }
6531
6532 runs
6533 })
6534}
6535
6536#[cfg(test)]
6537mod tests {
6538 use crate::test::{
6539 assert_text_with_selections, build_editor, select_ranges, EditorLspTestContext,
6540 EditorTestContext,
6541 };
6542
6543 use super::*;
6544 use futures::StreamExt;
6545 use gpui::{
6546 geometry::rect::RectF,
6547 platform::{WindowBounds, WindowOptions},
6548 };
6549 use indoc::indoc;
6550 use language::{FakeLspAdapter, LanguageConfig};
6551 use project::FakeFs;
6552 use settings::EditorSettings;
6553 use std::{cell::RefCell, rc::Rc, time::Instant};
6554 use text::Point;
6555 use unindent::Unindent;
6556 use util::{
6557 assert_set_eq,
6558 test::{
6559 marked_text_by, marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker,
6560 },
6561 };
6562 use workspace::{FollowableItem, ItemHandle, NavigationEntry, Pane};
6563
6564 #[gpui::test]
6565 fn test_edit_events(cx: &mut MutableAppContext) {
6566 cx.set_global(Settings::test(cx));
6567 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6568
6569 let events = Rc::new(RefCell::new(Vec::new()));
6570 let (_, editor1) = cx.add_window(Default::default(), {
6571 let events = events.clone();
6572 |cx| {
6573 cx.subscribe(&cx.handle(), move |_, _, event, _| {
6574 if matches!(
6575 event,
6576 Event::Edited | Event::BufferEdited | Event::DirtyChanged
6577 ) {
6578 events.borrow_mut().push(("editor1", *event));
6579 }
6580 })
6581 .detach();
6582 Editor::for_buffer(buffer.clone(), None, cx)
6583 }
6584 });
6585 let (_, editor2) = cx.add_window(Default::default(), {
6586 let events = events.clone();
6587 |cx| {
6588 cx.subscribe(&cx.handle(), move |_, _, event, _| {
6589 if matches!(
6590 event,
6591 Event::Edited | Event::BufferEdited | Event::DirtyChanged
6592 ) {
6593 events.borrow_mut().push(("editor2", *event));
6594 }
6595 })
6596 .detach();
6597 Editor::for_buffer(buffer.clone(), None, cx)
6598 }
6599 });
6600 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6601
6602 // Mutating editor 1 will emit an `Edited` event only for that editor.
6603 editor1.update(cx, |editor, cx| editor.insert("X", cx));
6604 assert_eq!(
6605 mem::take(&mut *events.borrow_mut()),
6606 [
6607 ("editor1", Event::Edited),
6608 ("editor1", Event::BufferEdited),
6609 ("editor2", Event::BufferEdited),
6610 ("editor1", Event::DirtyChanged),
6611 ("editor2", Event::DirtyChanged)
6612 ]
6613 );
6614
6615 // Mutating editor 2 will emit an `Edited` event only for that editor.
6616 editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
6617 assert_eq!(
6618 mem::take(&mut *events.borrow_mut()),
6619 [
6620 ("editor2", Event::Edited),
6621 ("editor1", Event::BufferEdited),
6622 ("editor2", Event::BufferEdited),
6623 ]
6624 );
6625
6626 // Undoing on editor 1 will emit an `Edited` event only for that editor.
6627 editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
6628 assert_eq!(
6629 mem::take(&mut *events.borrow_mut()),
6630 [
6631 ("editor1", Event::Edited),
6632 ("editor1", Event::BufferEdited),
6633 ("editor2", Event::BufferEdited),
6634 ("editor1", Event::DirtyChanged),
6635 ("editor2", Event::DirtyChanged),
6636 ]
6637 );
6638
6639 // Redoing on editor 1 will emit an `Edited` event only for that editor.
6640 editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
6641 assert_eq!(
6642 mem::take(&mut *events.borrow_mut()),
6643 [
6644 ("editor1", Event::Edited),
6645 ("editor1", Event::BufferEdited),
6646 ("editor2", Event::BufferEdited),
6647 ("editor1", Event::DirtyChanged),
6648 ("editor2", Event::DirtyChanged),
6649 ]
6650 );
6651
6652 // Undoing on editor 2 will emit an `Edited` event only for that editor.
6653 editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
6654 assert_eq!(
6655 mem::take(&mut *events.borrow_mut()),
6656 [
6657 ("editor2", Event::Edited),
6658 ("editor1", Event::BufferEdited),
6659 ("editor2", Event::BufferEdited),
6660 ("editor1", Event::DirtyChanged),
6661 ("editor2", Event::DirtyChanged),
6662 ]
6663 );
6664
6665 // Redoing on editor 2 will emit an `Edited` event only for that editor.
6666 editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
6667 assert_eq!(
6668 mem::take(&mut *events.borrow_mut()),
6669 [
6670 ("editor2", Event::Edited),
6671 ("editor1", Event::BufferEdited),
6672 ("editor2", Event::BufferEdited),
6673 ("editor1", Event::DirtyChanged),
6674 ("editor2", Event::DirtyChanged),
6675 ]
6676 );
6677
6678 // No event is emitted when the mutation is a no-op.
6679 editor2.update(cx, |editor, cx| {
6680 editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
6681
6682 editor.backspace(&Backspace, cx);
6683 });
6684 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6685 }
6686
6687 #[gpui::test]
6688 fn test_undo_redo_with_selection_restoration(cx: &mut MutableAppContext) {
6689 cx.set_global(Settings::test(cx));
6690 let mut now = Instant::now();
6691 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6692 let group_interval = buffer.read(cx).transaction_group_interval();
6693 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
6694 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6695
6696 editor.update(cx, |editor, cx| {
6697 editor.start_transaction_at(now, cx);
6698 editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
6699
6700 editor.insert("cd", cx);
6701 editor.end_transaction_at(now, cx);
6702 assert_eq!(editor.text(cx), "12cd56");
6703 assert_eq!(editor.selections.ranges(cx), vec![4..4]);
6704
6705 editor.start_transaction_at(now, cx);
6706 editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
6707 editor.insert("e", cx);
6708 editor.end_transaction_at(now, cx);
6709 assert_eq!(editor.text(cx), "12cde6");
6710 assert_eq!(editor.selections.ranges(cx), vec![5..5]);
6711
6712 now += group_interval + Duration::from_millis(1);
6713 editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
6714
6715 // Simulate an edit in another editor
6716 buffer.update(cx, |buffer, cx| {
6717 buffer.start_transaction_at(now, cx);
6718 buffer.edit([(0..1, "a")], None, cx);
6719 buffer.edit([(1..1, "b")], None, cx);
6720 buffer.end_transaction_at(now, cx);
6721 });
6722
6723 assert_eq!(editor.text(cx), "ab2cde6");
6724 assert_eq!(editor.selections.ranges(cx), vec![3..3]);
6725
6726 // Last transaction happened past the group interval in a different editor.
6727 // Undo it individually and don't restore selections.
6728 editor.undo(&Undo, cx);
6729 assert_eq!(editor.text(cx), "12cde6");
6730 assert_eq!(editor.selections.ranges(cx), vec![2..2]);
6731
6732 // First two transactions happened within the group interval in this editor.
6733 // Undo them together and restore selections.
6734 editor.undo(&Undo, cx);
6735 editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
6736 assert_eq!(editor.text(cx), "123456");
6737 assert_eq!(editor.selections.ranges(cx), vec![0..0]);
6738
6739 // Redo the first two transactions together.
6740 editor.redo(&Redo, cx);
6741 assert_eq!(editor.text(cx), "12cde6");
6742 assert_eq!(editor.selections.ranges(cx), vec![5..5]);
6743
6744 // Redo the last transaction on its own.
6745 editor.redo(&Redo, cx);
6746 assert_eq!(editor.text(cx), "ab2cde6");
6747 assert_eq!(editor.selections.ranges(cx), vec![6..6]);
6748
6749 // Test empty transactions.
6750 editor.start_transaction_at(now, cx);
6751 editor.end_transaction_at(now, cx);
6752 editor.undo(&Undo, cx);
6753 assert_eq!(editor.text(cx), "12cde6");
6754 });
6755 }
6756
6757 #[gpui::test]
6758 fn test_ime_composition(cx: &mut MutableAppContext) {
6759 cx.set_global(Settings::test(cx));
6760 let buffer = cx.add_model(|cx| {
6761 let mut buffer = language::Buffer::new(0, "abcde", cx);
6762 // Ensure automatic grouping doesn't occur.
6763 buffer.set_group_interval(Duration::ZERO);
6764 buffer
6765 });
6766
6767 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
6768 cx.add_window(Default::default(), |cx| {
6769 let mut editor = build_editor(buffer.clone(), cx);
6770
6771 // Start a new IME composition.
6772 editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
6773 editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
6774 editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
6775 assert_eq!(editor.text(cx), "äbcde");
6776 assert_eq!(
6777 editor.marked_text_ranges(cx),
6778 Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
6779 );
6780
6781 // Finalize IME composition.
6782 editor.replace_text_in_range(None, "ā", cx);
6783 assert_eq!(editor.text(cx), "ābcde");
6784 assert_eq!(editor.marked_text_ranges(cx), None);
6785
6786 // IME composition edits are grouped and are undone/redone at once.
6787 editor.undo(&Default::default(), cx);
6788 assert_eq!(editor.text(cx), "abcde");
6789 assert_eq!(editor.marked_text_ranges(cx), None);
6790 editor.redo(&Default::default(), cx);
6791 assert_eq!(editor.text(cx), "ābcde");
6792 assert_eq!(editor.marked_text_ranges(cx), None);
6793
6794 // Start a new IME composition.
6795 editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
6796 assert_eq!(
6797 editor.marked_text_ranges(cx),
6798 Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
6799 );
6800
6801 // Undoing during an IME composition cancels it.
6802 editor.undo(&Default::default(), cx);
6803 assert_eq!(editor.text(cx), "ābcde");
6804 assert_eq!(editor.marked_text_ranges(cx), None);
6805
6806 // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
6807 editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
6808 assert_eq!(editor.text(cx), "ābcdè");
6809 assert_eq!(
6810 editor.marked_text_ranges(cx),
6811 Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
6812 );
6813
6814 // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
6815 editor.replace_text_in_range(Some(4..999), "ę", cx);
6816 assert_eq!(editor.text(cx), "ābcdę");
6817 assert_eq!(editor.marked_text_ranges(cx), None);
6818
6819 // Start a new IME composition with multiple cursors.
6820 editor.change_selections(None, cx, |s| {
6821 s.select_ranges([
6822 OffsetUtf16(1)..OffsetUtf16(1),
6823 OffsetUtf16(3)..OffsetUtf16(3),
6824 OffsetUtf16(5)..OffsetUtf16(5),
6825 ])
6826 });
6827 editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
6828 assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
6829 assert_eq!(
6830 editor.marked_text_ranges(cx),
6831 Some(vec![
6832 OffsetUtf16(0)..OffsetUtf16(3),
6833 OffsetUtf16(4)..OffsetUtf16(7),
6834 OffsetUtf16(8)..OffsetUtf16(11)
6835 ])
6836 );
6837
6838 // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
6839 editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
6840 assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
6841 assert_eq!(
6842 editor.marked_text_ranges(cx),
6843 Some(vec![
6844 OffsetUtf16(1)..OffsetUtf16(2),
6845 OffsetUtf16(5)..OffsetUtf16(6),
6846 OffsetUtf16(9)..OffsetUtf16(10)
6847 ])
6848 );
6849
6850 // Finalize IME composition with multiple cursors.
6851 editor.replace_text_in_range(Some(9..10), "2", cx);
6852 assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
6853 assert_eq!(editor.marked_text_ranges(cx), None);
6854
6855 editor
6856 });
6857 }
6858
6859 #[gpui::test]
6860 fn test_selection_with_mouse(cx: &mut gpui::MutableAppContext) {
6861 cx.set_global(Settings::test(cx));
6862
6863 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
6864 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6865 editor.update(cx, |view, cx| {
6866 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
6867 });
6868 assert_eq!(
6869 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6870 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
6871 );
6872
6873 editor.update(cx, |view, cx| {
6874 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6875 });
6876
6877 assert_eq!(
6878 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6879 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6880 );
6881
6882 editor.update(cx, |view, cx| {
6883 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6884 });
6885
6886 assert_eq!(
6887 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6888 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
6889 );
6890
6891 editor.update(cx, |view, cx| {
6892 view.end_selection(cx);
6893 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6894 });
6895
6896 assert_eq!(
6897 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6898 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
6899 );
6900
6901 editor.update(cx, |view, cx| {
6902 view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx);
6903 view.update_selection(DisplayPoint::new(0, 0), 0, Vector2F::zero(), cx);
6904 });
6905
6906 assert_eq!(
6907 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6908 [
6909 DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
6910 DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
6911 ]
6912 );
6913
6914 editor.update(cx, |view, cx| {
6915 view.end_selection(cx);
6916 });
6917
6918 assert_eq!(
6919 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6920 [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
6921 );
6922 }
6923
6924 #[gpui::test]
6925 fn test_canceling_pending_selection(cx: &mut gpui::MutableAppContext) {
6926 cx.set_global(Settings::test(cx));
6927 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
6928 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6929
6930 view.update(cx, |view, cx| {
6931 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
6932 assert_eq!(
6933 view.selections.display_ranges(cx),
6934 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
6935 );
6936 });
6937
6938 view.update(cx, |view, cx| {
6939 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6940 assert_eq!(
6941 view.selections.display_ranges(cx),
6942 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6943 );
6944 });
6945
6946 view.update(cx, |view, cx| {
6947 view.cancel(&Cancel, cx);
6948 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6949 assert_eq!(
6950 view.selections.display_ranges(cx),
6951 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6952 );
6953 });
6954 }
6955
6956 #[gpui::test]
6957 fn test_clone(cx: &mut gpui::MutableAppContext) {
6958 let (text, selection_ranges) = marked_text_ranges(indoc! {"
6959 one
6960 two
6961 three[]
6962 four
6963 five[]
6964 "});
6965 cx.set_global(Settings::test(cx));
6966 let buffer = MultiBuffer::build_simple(&text, cx);
6967
6968 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6969
6970 editor.update(cx, |editor, cx| {
6971 editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
6972 editor.fold_ranges(
6973 [
6974 Point::new(1, 0)..Point::new(2, 0),
6975 Point::new(3, 0)..Point::new(4, 0),
6976 ],
6977 cx,
6978 );
6979 });
6980
6981 let (_, cloned_editor) = editor.update(cx, |editor, cx| {
6982 cx.add_window(Default::default(), |cx| editor.clone(cx))
6983 });
6984
6985 let snapshot = editor.update(cx, |e, cx| e.snapshot(cx));
6986 let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx));
6987
6988 assert_eq!(
6989 cloned_editor.update(cx, |e, cx| e.display_text(cx)),
6990 editor.update(cx, |e, cx| e.display_text(cx))
6991 );
6992 assert_eq!(
6993 cloned_snapshot
6994 .folds_in_range(0..text.len())
6995 .collect::<Vec<_>>(),
6996 snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
6997 );
6998 assert_set_eq!(
6999 cloned_editor.read(cx).selections.ranges::<Point>(cx),
7000 editor.read(cx).selections.ranges(cx)
7001 );
7002 assert_set_eq!(
7003 cloned_editor.update(cx, |e, cx| e.selections.display_ranges(cx)),
7004 editor.update(cx, |e, cx| e.selections.display_ranges(cx))
7005 );
7006 }
7007
7008 #[gpui::test]
7009 fn test_navigation_history(cx: &mut gpui::MutableAppContext) {
7010 cx.set_global(Settings::test(cx));
7011 use workspace::Item;
7012 let pane = cx.add_view(Default::default(), |cx| Pane::new(cx));
7013 let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
7014
7015 cx.add_window(Default::default(), |cx| {
7016 let mut editor = build_editor(buffer.clone(), cx);
7017 let handle = cx.handle();
7018 editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
7019
7020 fn pop_history(
7021 editor: &mut Editor,
7022 cx: &mut MutableAppContext,
7023 ) -> Option<NavigationEntry> {
7024 editor.nav_history.as_mut().unwrap().pop_backward(cx)
7025 }
7026
7027 // Move the cursor a small distance.
7028 // Nothing is added to the navigation history.
7029 editor.change_selections(None, cx, |s| {
7030 s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)])
7031 });
7032 editor.change_selections(None, cx, |s| {
7033 s.select_display_ranges([DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)])
7034 });
7035 assert!(pop_history(&mut editor, cx).is_none());
7036
7037 // Move the cursor a large distance.
7038 // The history can jump back to the previous position.
7039 editor.change_selections(None, cx, |s| {
7040 s.select_display_ranges([DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)])
7041 });
7042 let nav_entry = pop_history(&mut editor, cx).unwrap();
7043 editor.navigate(nav_entry.data.unwrap(), cx);
7044 assert_eq!(nav_entry.item.id(), cx.view_id());
7045 assert_eq!(
7046 editor.selections.display_ranges(cx),
7047 &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]
7048 );
7049 assert!(pop_history(&mut editor, cx).is_none());
7050
7051 // Move the cursor a small distance via the mouse.
7052 // Nothing is added to the navigation history.
7053 editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx);
7054 editor.end_selection(cx);
7055 assert_eq!(
7056 editor.selections.display_ranges(cx),
7057 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
7058 );
7059 assert!(pop_history(&mut editor, cx).is_none());
7060
7061 // Move the cursor a large distance via the mouse.
7062 // The history can jump back to the previous position.
7063 editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx);
7064 editor.end_selection(cx);
7065 assert_eq!(
7066 editor.selections.display_ranges(cx),
7067 &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]
7068 );
7069 let nav_entry = pop_history(&mut editor, cx).unwrap();
7070 editor.navigate(nav_entry.data.unwrap(), cx);
7071 assert_eq!(nav_entry.item.id(), cx.view_id());
7072 assert_eq!(
7073 editor.selections.display_ranges(cx),
7074 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
7075 );
7076 assert!(pop_history(&mut editor, cx).is_none());
7077
7078 // Set scroll position to check later
7079 editor.set_scroll_position(Vector2F::new(5.5, 5.5), cx);
7080 let original_scroll_position = editor.scroll_position;
7081 let original_scroll_top_anchor = editor.scroll_top_anchor.clone();
7082
7083 // Jump to the end of the document and adjust scroll
7084 editor.move_to_end(&MoveToEnd, cx);
7085 editor.set_scroll_position(Vector2F::new(-2.5, -0.5), cx);
7086 assert_ne!(editor.scroll_position, original_scroll_position);
7087 assert_ne!(editor.scroll_top_anchor, original_scroll_top_anchor);
7088
7089 let nav_entry = pop_history(&mut editor, cx).unwrap();
7090 editor.navigate(nav_entry.data.unwrap(), cx);
7091 assert_eq!(editor.scroll_position, original_scroll_position);
7092 assert_eq!(editor.scroll_top_anchor, original_scroll_top_anchor);
7093
7094 // Ensure we don't panic when navigation data contains invalid anchors *and* points.
7095 let mut invalid_anchor = editor.scroll_top_anchor.clone();
7096 invalid_anchor.text_anchor.buffer_id = Some(999);
7097 let invalid_point = Point::new(9999, 0);
7098 editor.navigate(
7099 Box::new(NavigationData {
7100 cursor_anchor: invalid_anchor.clone(),
7101 cursor_position: invalid_point,
7102 scroll_top_anchor: invalid_anchor.clone(),
7103 scroll_top_row: invalid_point.row,
7104 scroll_position: Default::default(),
7105 }),
7106 cx,
7107 );
7108 assert_eq!(
7109 editor.selections.display_ranges(cx),
7110 &[editor.max_point(cx)..editor.max_point(cx)]
7111 );
7112 assert_eq!(
7113 editor.scroll_position(cx),
7114 vec2f(0., editor.max_point(cx).row() as f32)
7115 );
7116
7117 editor
7118 });
7119 }
7120
7121 #[gpui::test]
7122 fn test_cancel(cx: &mut gpui::MutableAppContext) {
7123 cx.set_global(Settings::test(cx));
7124 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
7125 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7126
7127 view.update(cx, |view, cx| {
7128 view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
7129 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
7130 view.end_selection(cx);
7131
7132 view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx);
7133 view.update_selection(DisplayPoint::new(0, 3), 0, Vector2F::zero(), cx);
7134 view.end_selection(cx);
7135 assert_eq!(
7136 view.selections.display_ranges(cx),
7137 [
7138 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
7139 DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1),
7140 ]
7141 );
7142 });
7143
7144 view.update(cx, |view, cx| {
7145 view.cancel(&Cancel, cx);
7146 assert_eq!(
7147 view.selections.display_ranges(cx),
7148 [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)]
7149 );
7150 });
7151
7152 view.update(cx, |view, cx| {
7153 view.cancel(&Cancel, cx);
7154 assert_eq!(
7155 view.selections.display_ranges(cx),
7156 [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)]
7157 );
7158 });
7159 }
7160
7161 #[gpui::test]
7162 fn test_fold(cx: &mut gpui::MutableAppContext) {
7163 cx.set_global(Settings::test(cx));
7164 let buffer = MultiBuffer::build_simple(
7165 &"
7166 impl Foo {
7167 // Hello!
7168
7169 fn a() {
7170 1
7171 }
7172
7173 fn b() {
7174 2
7175 }
7176
7177 fn c() {
7178 3
7179 }
7180 }
7181 "
7182 .unindent(),
7183 cx,
7184 );
7185 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7186
7187 view.update(cx, |view, cx| {
7188 view.change_selections(None, cx, |s| {
7189 s.select_display_ranges([DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)]);
7190 });
7191 view.fold(&Fold, cx);
7192 assert_eq!(
7193 view.display_text(cx),
7194 "
7195 impl Foo {
7196 // Hello!
7197
7198 fn a() {
7199 1
7200 }
7201
7202 fn b() {…
7203 }
7204
7205 fn c() {…
7206 }
7207 }
7208 "
7209 .unindent(),
7210 );
7211
7212 view.fold(&Fold, cx);
7213 assert_eq!(
7214 view.display_text(cx),
7215 "
7216 impl Foo {…
7217 }
7218 "
7219 .unindent(),
7220 );
7221
7222 view.unfold_lines(&UnfoldLines, cx);
7223 assert_eq!(
7224 view.display_text(cx),
7225 "
7226 impl Foo {
7227 // Hello!
7228
7229 fn a() {
7230 1
7231 }
7232
7233 fn b() {…
7234 }
7235
7236 fn c() {…
7237 }
7238 }
7239 "
7240 .unindent(),
7241 );
7242
7243 view.unfold_lines(&UnfoldLines, cx);
7244 assert_eq!(view.display_text(cx), buffer.read(cx).read(cx).text());
7245 });
7246 }
7247
7248 #[gpui::test]
7249 fn test_move_cursor(cx: &mut gpui::MutableAppContext) {
7250 cx.set_global(Settings::test(cx));
7251 let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
7252 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7253
7254 buffer.update(cx, |buffer, cx| {
7255 buffer.edit(
7256 vec![
7257 (Point::new(1, 0)..Point::new(1, 0), "\t"),
7258 (Point::new(1, 1)..Point::new(1, 1), "\t"),
7259 ],
7260 None,
7261 cx,
7262 );
7263 });
7264
7265 view.update(cx, |view, cx| {
7266 assert_eq!(
7267 view.selections.display_ranges(cx),
7268 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
7269 );
7270
7271 view.move_down(&MoveDown, cx);
7272 assert_eq!(
7273 view.selections.display_ranges(cx),
7274 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
7275 );
7276
7277 view.move_right(&MoveRight, cx);
7278 assert_eq!(
7279 view.selections.display_ranges(cx),
7280 &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
7281 );
7282
7283 view.move_left(&MoveLeft, cx);
7284 assert_eq!(
7285 view.selections.display_ranges(cx),
7286 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
7287 );
7288
7289 view.move_up(&MoveUp, cx);
7290 assert_eq!(
7291 view.selections.display_ranges(cx),
7292 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
7293 );
7294
7295 view.move_to_end(&MoveToEnd, cx);
7296 assert_eq!(
7297 view.selections.display_ranges(cx),
7298 &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)]
7299 );
7300
7301 view.move_to_beginning(&MoveToBeginning, cx);
7302 assert_eq!(
7303 view.selections.display_ranges(cx),
7304 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
7305 );
7306
7307 view.change_selections(None, cx, |s| {
7308 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]);
7309 });
7310 view.select_to_beginning(&SelectToBeginning, cx);
7311 assert_eq!(
7312 view.selections.display_ranges(cx),
7313 &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)]
7314 );
7315
7316 view.select_to_end(&SelectToEnd, cx);
7317 assert_eq!(
7318 view.selections.display_ranges(cx),
7319 &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)]
7320 );
7321 });
7322 }
7323
7324 #[gpui::test]
7325 fn test_move_cursor_multibyte(cx: &mut gpui::MutableAppContext) {
7326 cx.set_global(Settings::test(cx));
7327 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx);
7328 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7329
7330 assert_eq!('ⓐ'.len_utf8(), 3);
7331 assert_eq!('α'.len_utf8(), 2);
7332
7333 view.update(cx, |view, cx| {
7334 view.fold_ranges(
7335 vec![
7336 Point::new(0, 6)..Point::new(0, 12),
7337 Point::new(1, 2)..Point::new(1, 4),
7338 Point::new(2, 4)..Point::new(2, 8),
7339 ],
7340 cx,
7341 );
7342 assert_eq!(view.display_text(cx), "ⓐⓑ…ⓔ\nab…e\nαβ…ε\n");
7343
7344 view.move_right(&MoveRight, cx);
7345 assert_eq!(
7346 view.selections.display_ranges(cx),
7347 &[empty_range(0, "ⓐ".len())]
7348 );
7349 view.move_right(&MoveRight, cx);
7350 assert_eq!(
7351 view.selections.display_ranges(cx),
7352 &[empty_range(0, "ⓐⓑ".len())]
7353 );
7354 view.move_right(&MoveRight, cx);
7355 assert_eq!(
7356 view.selections.display_ranges(cx),
7357 &[empty_range(0, "ⓐⓑ…".len())]
7358 );
7359
7360 view.move_down(&MoveDown, cx);
7361 assert_eq!(
7362 view.selections.display_ranges(cx),
7363 &[empty_range(1, "ab…".len())]
7364 );
7365 view.move_left(&MoveLeft, cx);
7366 assert_eq!(
7367 view.selections.display_ranges(cx),
7368 &[empty_range(1, "ab".len())]
7369 );
7370 view.move_left(&MoveLeft, cx);
7371 assert_eq!(
7372 view.selections.display_ranges(cx),
7373 &[empty_range(1, "a".len())]
7374 );
7375
7376 view.move_down(&MoveDown, cx);
7377 assert_eq!(
7378 view.selections.display_ranges(cx),
7379 &[empty_range(2, "α".len())]
7380 );
7381 view.move_right(&MoveRight, cx);
7382 assert_eq!(
7383 view.selections.display_ranges(cx),
7384 &[empty_range(2, "αβ".len())]
7385 );
7386 view.move_right(&MoveRight, cx);
7387 assert_eq!(
7388 view.selections.display_ranges(cx),
7389 &[empty_range(2, "αβ…".len())]
7390 );
7391 view.move_right(&MoveRight, cx);
7392 assert_eq!(
7393 view.selections.display_ranges(cx),
7394 &[empty_range(2, "αβ…ε".len())]
7395 );
7396
7397 view.move_up(&MoveUp, cx);
7398 assert_eq!(
7399 view.selections.display_ranges(cx),
7400 &[empty_range(1, "ab…e".len())]
7401 );
7402 view.move_up(&MoveUp, cx);
7403 assert_eq!(
7404 view.selections.display_ranges(cx),
7405 &[empty_range(0, "ⓐⓑ…ⓔ".len())]
7406 );
7407 view.move_left(&MoveLeft, cx);
7408 assert_eq!(
7409 view.selections.display_ranges(cx),
7410 &[empty_range(0, "ⓐⓑ…".len())]
7411 );
7412 view.move_left(&MoveLeft, cx);
7413 assert_eq!(
7414 view.selections.display_ranges(cx),
7415 &[empty_range(0, "ⓐⓑ".len())]
7416 );
7417 view.move_left(&MoveLeft, cx);
7418 assert_eq!(
7419 view.selections.display_ranges(cx),
7420 &[empty_range(0, "ⓐ".len())]
7421 );
7422 });
7423 }
7424
7425 #[gpui::test]
7426 fn test_move_cursor_different_line_lengths(cx: &mut gpui::MutableAppContext) {
7427 cx.set_global(Settings::test(cx));
7428 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
7429 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7430 view.update(cx, |view, cx| {
7431 view.change_selections(None, cx, |s| {
7432 s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
7433 });
7434 view.move_down(&MoveDown, cx);
7435 assert_eq!(
7436 view.selections.display_ranges(cx),
7437 &[empty_range(1, "abcd".len())]
7438 );
7439
7440 view.move_down(&MoveDown, cx);
7441 assert_eq!(
7442 view.selections.display_ranges(cx),
7443 &[empty_range(2, "αβγ".len())]
7444 );
7445
7446 view.move_down(&MoveDown, cx);
7447 assert_eq!(
7448 view.selections.display_ranges(cx),
7449 &[empty_range(3, "abcd".len())]
7450 );
7451
7452 view.move_down(&MoveDown, cx);
7453 assert_eq!(
7454 view.selections.display_ranges(cx),
7455 &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
7456 );
7457
7458 view.move_up(&MoveUp, cx);
7459 assert_eq!(
7460 view.selections.display_ranges(cx),
7461 &[empty_range(3, "abcd".len())]
7462 );
7463
7464 view.move_up(&MoveUp, cx);
7465 assert_eq!(
7466 view.selections.display_ranges(cx),
7467 &[empty_range(2, "αβγ".len())]
7468 );
7469 });
7470 }
7471
7472 #[gpui::test]
7473 fn test_beginning_end_of_line(cx: &mut gpui::MutableAppContext) {
7474 cx.set_global(Settings::test(cx));
7475 let buffer = MultiBuffer::build_simple("abc\n def", cx);
7476 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7477 view.update(cx, |view, cx| {
7478 view.change_selections(None, cx, |s| {
7479 s.select_display_ranges([
7480 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7481 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
7482 ]);
7483 });
7484 });
7485
7486 view.update(cx, |view, cx| {
7487 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7488 assert_eq!(
7489 view.selections.display_ranges(cx),
7490 &[
7491 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7492 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7493 ]
7494 );
7495 });
7496
7497 view.update(cx, |view, cx| {
7498 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7499 assert_eq!(
7500 view.selections.display_ranges(cx),
7501 &[
7502 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7503 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7504 ]
7505 );
7506 });
7507
7508 view.update(cx, |view, cx| {
7509 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7510 assert_eq!(
7511 view.selections.display_ranges(cx),
7512 &[
7513 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7514 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7515 ]
7516 );
7517 });
7518
7519 view.update(cx, |view, cx| {
7520 view.move_to_end_of_line(&MoveToEndOfLine, cx);
7521 assert_eq!(
7522 view.selections.display_ranges(cx),
7523 &[
7524 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7525 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
7526 ]
7527 );
7528 });
7529
7530 // Moving to the end of line again is a no-op.
7531 view.update(cx, |view, cx| {
7532 view.move_to_end_of_line(&MoveToEndOfLine, cx);
7533 assert_eq!(
7534 view.selections.display_ranges(cx),
7535 &[
7536 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7537 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
7538 ]
7539 );
7540 });
7541
7542 view.update(cx, |view, cx| {
7543 view.move_left(&MoveLeft, cx);
7544 view.select_to_beginning_of_line(
7545 &SelectToBeginningOfLine {
7546 stop_at_soft_wraps: true,
7547 },
7548 cx,
7549 );
7550 assert_eq!(
7551 view.selections.display_ranges(cx),
7552 &[
7553 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7554 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7555 ]
7556 );
7557 });
7558
7559 view.update(cx, |view, cx| {
7560 view.select_to_beginning_of_line(
7561 &SelectToBeginningOfLine {
7562 stop_at_soft_wraps: true,
7563 },
7564 cx,
7565 );
7566 assert_eq!(
7567 view.selections.display_ranges(cx),
7568 &[
7569 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7570 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0),
7571 ]
7572 );
7573 });
7574
7575 view.update(cx, |view, cx| {
7576 view.select_to_beginning_of_line(
7577 &SelectToBeginningOfLine {
7578 stop_at_soft_wraps: true,
7579 },
7580 cx,
7581 );
7582 assert_eq!(
7583 view.selections.display_ranges(cx),
7584 &[
7585 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7586 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7587 ]
7588 );
7589 });
7590
7591 view.update(cx, |view, cx| {
7592 view.select_to_end_of_line(
7593 &SelectToEndOfLine {
7594 stop_at_soft_wraps: true,
7595 },
7596 cx,
7597 );
7598 assert_eq!(
7599 view.selections.display_ranges(cx),
7600 &[
7601 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
7602 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5),
7603 ]
7604 );
7605 });
7606
7607 view.update(cx, |view, cx| {
7608 view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
7609 assert_eq!(view.display_text(cx), "ab\n de");
7610 assert_eq!(
7611 view.selections.display_ranges(cx),
7612 &[
7613 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7614 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
7615 ]
7616 );
7617 });
7618
7619 view.update(cx, |view, cx| {
7620 view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
7621 assert_eq!(view.display_text(cx), "\n");
7622 assert_eq!(
7623 view.selections.display_ranges(cx),
7624 &[
7625 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7626 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7627 ]
7628 );
7629 });
7630 }
7631
7632 #[gpui::test]
7633 fn test_prev_next_word_boundary(cx: &mut gpui::MutableAppContext) {
7634 cx.set_global(Settings::test(cx));
7635 let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx);
7636 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7637 view.update(cx, |view, cx| {
7638 view.change_selections(None, cx, |s| {
7639 s.select_display_ranges([
7640 DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
7641 DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
7642 ])
7643 });
7644
7645 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7646 assert_selection_ranges(
7647 "use std::<>str::{foo, bar}\n\n {[]baz.qux()}",
7648 vec![('<', '>'), ('[', ']')],
7649 view,
7650 cx,
7651 );
7652
7653 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7654 assert_selection_ranges(
7655 "use std<>::str::{foo, bar}\n\n []{baz.qux()}",
7656 vec![('<', '>'), ('[', ']')],
7657 view,
7658 cx,
7659 );
7660
7661 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7662 assert_selection_ranges(
7663 "use <>std::str::{foo, bar}\n\n[] {baz.qux()}",
7664 vec![('<', '>'), ('[', ']')],
7665 view,
7666 cx,
7667 );
7668
7669 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7670 assert_selection_ranges(
7671 "<>use std::str::{foo, bar}\n[]\n {baz.qux()}",
7672 vec![('<', '>'), ('[', ']')],
7673 view,
7674 cx,
7675 );
7676
7677 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7678 assert_selection_ranges(
7679 "<>use std::str::{foo, bar[]}\n\n {baz.qux()}",
7680 vec![('<', '>'), ('[', ']')],
7681 view,
7682 cx,
7683 );
7684
7685 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7686 assert_selection_ranges(
7687 "use<> std::str::{foo, bar}[]\n\n {baz.qux()}",
7688 vec![('<', '>'), ('[', ']')],
7689 view,
7690 cx,
7691 );
7692
7693 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7694 assert_selection_ranges(
7695 "use std<>::str::{foo, bar}\n[]\n {baz.qux()}",
7696 vec![('<', '>'), ('[', ']')],
7697 view,
7698 cx,
7699 );
7700
7701 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7702 assert_selection_ranges(
7703 "use std::<>str::{foo, bar}\n\n {[]baz.qux()}",
7704 vec![('<', '>'), ('[', ']')],
7705 view,
7706 cx,
7707 );
7708
7709 view.move_right(&MoveRight, cx);
7710 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7711 assert_selection_ranges(
7712 "use std::>s<tr::{foo, bar}\n\n {]b[az.qux()}",
7713 vec![('<', '>'), ('[', ']')],
7714 view,
7715 cx,
7716 );
7717
7718 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7719 assert_selection_ranges(
7720 "use std>::s<tr::{foo, bar}\n\n ]{b[az.qux()}",
7721 vec![('<', '>'), ('[', ']')],
7722 view,
7723 cx,
7724 );
7725
7726 view.select_to_next_word_end(&SelectToNextWordEnd, cx);
7727 assert_selection_ranges(
7728 "use std::>s<tr::{foo, bar}\n\n {]b[az.qux()}",
7729 vec![('<', '>'), ('[', ']')],
7730 view,
7731 cx,
7732 );
7733 });
7734 }
7735
7736 #[gpui::test]
7737 fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut gpui::MutableAppContext) {
7738 cx.set_global(Settings::test(cx));
7739 let buffer = MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx);
7740 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7741
7742 view.update(cx, |view, cx| {
7743 view.set_wrap_width(Some(140.), cx);
7744 assert_eq!(
7745 view.display_text(cx),
7746 "use one::{\n two::three::\n four::five\n};"
7747 );
7748
7749 view.change_selections(None, cx, |s| {
7750 s.select_display_ranges([DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)]);
7751 });
7752
7753 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7754 assert_eq!(
7755 view.selections.display_ranges(cx),
7756 &[DisplayPoint::new(1, 9)..DisplayPoint::new(1, 9)]
7757 );
7758
7759 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7760 assert_eq!(
7761 view.selections.display_ranges(cx),
7762 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
7763 );
7764
7765 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7766 assert_eq!(
7767 view.selections.display_ranges(cx),
7768 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7769 );
7770
7771 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7772 assert_eq!(
7773 view.selections.display_ranges(cx),
7774 &[DisplayPoint::new(2, 8)..DisplayPoint::new(2, 8)]
7775 );
7776
7777 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7778 assert_eq!(
7779 view.selections.display_ranges(cx),
7780 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7781 );
7782
7783 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7784 assert_eq!(
7785 view.selections.display_ranges(cx),
7786 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
7787 );
7788 });
7789 }
7790
7791 #[gpui::test]
7792 fn test_delete_to_beginning_of_line(cx: &mut gpui::MutableAppContext) {
7793 cx.set_global(Settings::test(cx));
7794 let (text, ranges) = marked_text_ranges("one [two three] four");
7795 let buffer = MultiBuffer::build_simple(&text, cx);
7796
7797 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7798
7799 editor.update(cx, |editor, cx| {
7800 editor.change_selections(None, cx, |s| s.select_ranges(ranges));
7801 editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
7802 assert_eq!(editor.text(cx), " four");
7803 });
7804 }
7805
7806 #[gpui::test]
7807 fn test_delete_to_word_boundary(cx: &mut gpui::MutableAppContext) {
7808 cx.set_global(Settings::test(cx));
7809 let buffer = MultiBuffer::build_simple("one two three four", cx);
7810 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7811
7812 view.update(cx, |view, cx| {
7813 view.change_selections(None, cx, |s| {
7814 s.select_display_ranges([
7815 // an empty selection - the preceding word fragment is deleted
7816 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7817 // characters selected - they are deleted
7818 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 12),
7819 ])
7820 });
7821 view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
7822 });
7823
7824 assert_eq!(buffer.read(cx).read(cx).text(), "e two te four");
7825
7826 view.update(cx, |view, cx| {
7827 view.change_selections(None, cx, |s| {
7828 s.select_display_ranges([
7829 // an empty selection - the following word fragment is deleted
7830 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7831 // characters selected - they are deleted
7832 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 10),
7833 ])
7834 });
7835 view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
7836 });
7837
7838 assert_eq!(buffer.read(cx).read(cx).text(), "e t te our");
7839 }
7840
7841 #[gpui::test]
7842 fn test_newline(cx: &mut gpui::MutableAppContext) {
7843 cx.set_global(Settings::test(cx));
7844 let buffer = MultiBuffer::build_simple("aaaa\n bbbb\n", cx);
7845 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7846
7847 view.update(cx, |view, cx| {
7848 view.change_selections(None, cx, |s| {
7849 s.select_display_ranges([
7850 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7851 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7852 DisplayPoint::new(1, 6)..DisplayPoint::new(1, 6),
7853 ])
7854 });
7855
7856 view.newline(&Newline, cx);
7857 assert_eq!(view.text(cx), "aa\naa\n \n bb\n bb\n");
7858 });
7859 }
7860
7861 #[gpui::test]
7862 fn test_newline_with_old_selections(cx: &mut gpui::MutableAppContext) {
7863 cx.set_global(Settings::test(cx));
7864 let buffer = MultiBuffer::build_simple(
7865 "
7866 a
7867 b(
7868 X
7869 )
7870 c(
7871 X
7872 )
7873 "
7874 .unindent()
7875 .as_str(),
7876 cx,
7877 );
7878
7879 let (_, editor) = cx.add_window(Default::default(), |cx| {
7880 let mut editor = build_editor(buffer.clone(), cx);
7881 editor.change_selections(None, cx, |s| {
7882 s.select_ranges([
7883 Point::new(2, 4)..Point::new(2, 5),
7884 Point::new(5, 4)..Point::new(5, 5),
7885 ])
7886 });
7887 editor
7888 });
7889
7890 // Edit the buffer directly, deleting ranges surrounding the editor's selections
7891 buffer.update(cx, |buffer, cx| {
7892 buffer.edit(
7893 [
7894 (Point::new(1, 2)..Point::new(3, 0), ""),
7895 (Point::new(4, 2)..Point::new(6, 0), ""),
7896 ],
7897 None,
7898 cx,
7899 );
7900 assert_eq!(
7901 buffer.read(cx).text(),
7902 "
7903 a
7904 b()
7905 c()
7906 "
7907 .unindent()
7908 );
7909 });
7910
7911 editor.update(cx, |editor, cx| {
7912 assert_eq!(
7913 editor.selections.ranges(cx),
7914 &[
7915 Point::new(1, 2)..Point::new(1, 2),
7916 Point::new(2, 2)..Point::new(2, 2),
7917 ],
7918 );
7919
7920 editor.newline(&Newline, cx);
7921 assert_eq!(
7922 editor.text(cx),
7923 "
7924 a
7925 b(
7926 )
7927 c(
7928 )
7929 "
7930 .unindent()
7931 );
7932
7933 // The selections are moved after the inserted newlines
7934 assert_eq!(
7935 editor.selections.ranges(cx),
7936 &[
7937 Point::new(2, 0)..Point::new(2, 0),
7938 Point::new(4, 0)..Point::new(4, 0),
7939 ],
7940 );
7941 });
7942 }
7943
7944 #[gpui::test]
7945 fn test_insert_with_old_selections(cx: &mut gpui::MutableAppContext) {
7946 cx.set_global(Settings::test(cx));
7947 let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
7948 let (_, editor) = cx.add_window(Default::default(), |cx| {
7949 let mut editor = build_editor(buffer.clone(), cx);
7950 editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
7951 editor
7952 });
7953
7954 // Edit the buffer directly, deleting ranges surrounding the editor's selections
7955 buffer.update(cx, |buffer, cx| {
7956 buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
7957 assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
7958 });
7959
7960 editor.update(cx, |editor, cx| {
7961 assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
7962
7963 editor.insert("Z", cx);
7964 assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
7965
7966 // The selections are moved after the inserted characters
7967 assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
7968 });
7969 }
7970
7971 #[gpui::test]
7972 async fn test_tab(cx: &mut gpui::TestAppContext) {
7973 let mut cx = EditorTestContext::new(cx).await;
7974 cx.update(|cx| {
7975 cx.update_global::<Settings, _, _>(|settings, _| {
7976 settings.editor_overrides.tab_size = Some(NonZeroU32::new(3).unwrap());
7977 });
7978 });
7979 cx.set_state(indoc! {"
7980 |ab|c
7981 |🏀|🏀|efg
7982 d|
7983 "});
7984 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7985 cx.assert_editor_state(indoc! {"
7986 |ab |c
7987 |🏀 |🏀 |efg
7988 d |
7989 "});
7990 }
7991
7992 #[gpui::test]
7993 async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
7994 let mut cx = EditorTestContext::new(cx).await;
7995
7996 cx.set_state(indoc! {"
7997 [one} [two}
7998 three
7999 four"});
8000 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8001 cx.assert_editor_state(indoc! {"
8002 [one} [two}
8003 three
8004 four"});
8005
8006 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8007 cx.assert_editor_state(indoc! {"
8008 [one} [two}
8009 three
8010 four"});
8011
8012 // select across line ending
8013 cx.set_state(indoc! {"
8014 one two
8015 t[hree
8016 } four"});
8017 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8018 cx.assert_editor_state(indoc! {"
8019 one two
8020 t[hree
8021 } four"});
8022
8023 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8024 cx.assert_editor_state(indoc! {"
8025 one two
8026 t[hree
8027 } four"});
8028
8029 // Ensure that indenting/outdenting works when the cursor is at column 0.
8030 cx.set_state(indoc! {"
8031 one two
8032 |three
8033 four"});
8034 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8035 cx.assert_editor_state(indoc! {"
8036 one two
8037 |three
8038 four"});
8039
8040 cx.set_state(indoc! {"
8041 one two
8042 | three
8043 four"});
8044 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8045 cx.assert_editor_state(indoc! {"
8046 one two
8047 |three
8048 four"});
8049 }
8050
8051 #[gpui::test]
8052 async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
8053 let mut cx = EditorTestContext::new(cx).await;
8054 cx.update(|cx| {
8055 cx.update_global::<Settings, _, _>(|settings, _| {
8056 settings.editor_overrides.hard_tabs = Some(true);
8057 });
8058 });
8059
8060 // select two ranges on one line
8061 cx.set_state(indoc! {"
8062 [one} [two}
8063 three
8064 four"});
8065 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8066 cx.assert_editor_state(indoc! {"
8067 \t[one} [two}
8068 three
8069 four"});
8070 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8071 cx.assert_editor_state(indoc! {"
8072 \t\t[one} [two}
8073 three
8074 four"});
8075 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8076 cx.assert_editor_state(indoc! {"
8077 \t[one} [two}
8078 three
8079 four"});
8080 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8081 cx.assert_editor_state(indoc! {"
8082 [one} [two}
8083 three
8084 four"});
8085
8086 // select across a line ending
8087 cx.set_state(indoc! {"
8088 one two
8089 t[hree
8090 }four"});
8091 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8092 cx.assert_editor_state(indoc! {"
8093 one two
8094 \tt[hree
8095 }four"});
8096 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8097 cx.assert_editor_state(indoc! {"
8098 one two
8099 \t\tt[hree
8100 }four"});
8101 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8102 cx.assert_editor_state(indoc! {"
8103 one two
8104 \tt[hree
8105 }four"});
8106 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8107 cx.assert_editor_state(indoc! {"
8108 one two
8109 t[hree
8110 }four"});
8111
8112 // Ensure that indenting/outdenting works when the cursor is at column 0.
8113 cx.set_state(indoc! {"
8114 one two
8115 |three
8116 four"});
8117 cx.assert_editor_state(indoc! {"
8118 one two
8119 |three
8120 four"});
8121 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8122 cx.assert_editor_state(indoc! {"
8123 one two
8124 \t|three
8125 four"});
8126 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8127 cx.assert_editor_state(indoc! {"
8128 one two
8129 |three
8130 four"});
8131 }
8132
8133 #[gpui::test]
8134 fn test_indent_outdent_with_excerpts(cx: &mut gpui::MutableAppContext) {
8135 cx.set_global(
8136 Settings::test(cx)
8137 .with_language_defaults(
8138 "TOML",
8139 EditorSettings {
8140 tab_size: Some(2.try_into().unwrap()),
8141 ..Default::default()
8142 },
8143 )
8144 .with_language_defaults(
8145 "Rust",
8146 EditorSettings {
8147 tab_size: Some(4.try_into().unwrap()),
8148 ..Default::default()
8149 },
8150 ),
8151 );
8152 let toml_language = Arc::new(Language::new(
8153 LanguageConfig {
8154 name: "TOML".into(),
8155 ..Default::default()
8156 },
8157 None,
8158 ));
8159 let rust_language = Arc::new(Language::new(
8160 LanguageConfig {
8161 name: "Rust".into(),
8162 ..Default::default()
8163 },
8164 None,
8165 ));
8166
8167 let toml_buffer = cx
8168 .add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx).with_language(toml_language, cx));
8169 let rust_buffer = cx.add_model(|cx| {
8170 Buffer::new(0, "const c: usize = 3;\n", cx).with_language(rust_language, cx)
8171 });
8172 let multibuffer = cx.add_model(|cx| {
8173 let mut multibuffer = MultiBuffer::new(0);
8174 multibuffer.push_excerpts(
8175 toml_buffer.clone(),
8176 [ExcerptRange {
8177 context: Point::new(0, 0)..Point::new(2, 0),
8178 primary: None,
8179 }],
8180 cx,
8181 );
8182 multibuffer.push_excerpts(
8183 rust_buffer.clone(),
8184 [ExcerptRange {
8185 context: Point::new(0, 0)..Point::new(1, 0),
8186 primary: None,
8187 }],
8188 cx,
8189 );
8190 multibuffer
8191 });
8192
8193 cx.add_window(Default::default(), |cx| {
8194 let mut editor = build_editor(multibuffer, cx);
8195
8196 assert_eq!(
8197 editor.text(cx),
8198 indoc! {"
8199 a = 1
8200 b = 2
8201
8202 const c: usize = 3;
8203 "}
8204 );
8205
8206 select_ranges(
8207 &mut editor,
8208 indoc! {"
8209 [a] = 1
8210 b = 2
8211
8212 [const c:] usize = 3;
8213 "},
8214 cx,
8215 );
8216
8217 editor.tab(&Tab, cx);
8218 assert_text_with_selections(
8219 &mut editor,
8220 indoc! {"
8221 [a] = 1
8222 b = 2
8223
8224 [const c:] usize = 3;
8225 "},
8226 cx,
8227 );
8228 editor.tab_prev(&TabPrev, cx);
8229 assert_text_with_selections(
8230 &mut editor,
8231 indoc! {"
8232 [a] = 1
8233 b = 2
8234
8235 [const c:] usize = 3;
8236 "},
8237 cx,
8238 );
8239
8240 editor
8241 });
8242 }
8243
8244 #[gpui::test]
8245 async fn test_backspace(cx: &mut gpui::TestAppContext) {
8246 let mut cx = EditorTestContext::new(cx).await;
8247 // Basic backspace
8248 cx.set_state(indoc! {"
8249 on|e two three
8250 fou[r} five six
8251 seven {eight nine
8252 ]ten"});
8253 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
8254 cx.assert_editor_state(indoc! {"
8255 o|e two three
8256 fou| five six
8257 seven |ten"});
8258
8259 // Test backspace inside and around indents
8260 cx.set_state(indoc! {"
8261 zero
8262 |one
8263 |two
8264 | | | three
8265 | | four"});
8266 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
8267 cx.assert_editor_state(indoc! {"
8268 zero
8269 |one
8270 |two
8271 | three| four"});
8272
8273 // Test backspace with line_mode set to true
8274 cx.update_editor(|e, _| e.selections.line_mode = true);
8275 cx.set_state(indoc! {"
8276 The |quick |brown
8277 fox jumps over
8278 the lazy dog
8279 |The qu[ick b}rown"});
8280 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
8281 cx.assert_editor_state(indoc! {"
8282 |fox jumps over
8283 the lazy dog|"});
8284 }
8285
8286 #[gpui::test]
8287 async fn test_delete(cx: &mut gpui::TestAppContext) {
8288 let mut cx = EditorTestContext::new(cx).await;
8289
8290 cx.set_state(indoc! {"
8291 on|e two three
8292 fou[r} five six
8293 seven {eight nine
8294 ]ten"});
8295 cx.update_editor(|e, cx| e.delete(&Delete, cx));
8296 cx.assert_editor_state(indoc! {"
8297 on| two three
8298 fou| five six
8299 seven |ten"});
8300
8301 // Test backspace with line_mode set to true
8302 cx.update_editor(|e, _| e.selections.line_mode = true);
8303 cx.set_state(indoc! {"
8304 The |quick |brown
8305 fox {jum]ps over
8306 the lazy dog
8307 |The qu[ick b}rown"});
8308 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
8309 cx.assert_editor_state("|the lazy dog|");
8310 }
8311
8312 #[gpui::test]
8313 fn test_delete_line(cx: &mut gpui::MutableAppContext) {
8314 cx.set_global(Settings::test(cx));
8315 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8316 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8317 view.update(cx, |view, cx| {
8318 view.change_selections(None, cx, |s| {
8319 s.select_display_ranges([
8320 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8321 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
8322 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8323 ])
8324 });
8325 view.delete_line(&DeleteLine, cx);
8326 assert_eq!(view.display_text(cx), "ghi");
8327 assert_eq!(
8328 view.selections.display_ranges(cx),
8329 vec![
8330 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
8331 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
8332 ]
8333 );
8334 });
8335
8336 cx.set_global(Settings::test(cx));
8337 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8338 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8339 view.update(cx, |view, cx| {
8340 view.change_selections(None, cx, |s| {
8341 s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)])
8342 });
8343 view.delete_line(&DeleteLine, cx);
8344 assert_eq!(view.display_text(cx), "ghi\n");
8345 assert_eq!(
8346 view.selections.display_ranges(cx),
8347 vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
8348 );
8349 });
8350 }
8351
8352 #[gpui::test]
8353 fn test_duplicate_line(cx: &mut gpui::MutableAppContext) {
8354 cx.set_global(Settings::test(cx));
8355 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8356 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8357 view.update(cx, |view, cx| {
8358 view.change_selections(None, cx, |s| {
8359 s.select_display_ranges([
8360 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8361 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8362 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8363 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8364 ])
8365 });
8366 view.duplicate_line(&DuplicateLine, cx);
8367 assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
8368 assert_eq!(
8369 view.selections.display_ranges(cx),
8370 vec![
8371 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
8372 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
8373 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8374 DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
8375 ]
8376 );
8377 });
8378
8379 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8380 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8381 view.update(cx, |view, cx| {
8382 view.change_selections(None, cx, |s| {
8383 s.select_display_ranges([
8384 DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
8385 DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
8386 ])
8387 });
8388 view.duplicate_line(&DuplicateLine, cx);
8389 assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
8390 assert_eq!(
8391 view.selections.display_ranges(cx),
8392 vec![
8393 DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
8394 DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
8395 ]
8396 );
8397 });
8398 }
8399
8400 #[gpui::test]
8401 fn test_move_line_up_down(cx: &mut gpui::MutableAppContext) {
8402 cx.set_global(Settings::test(cx));
8403 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
8404 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8405 view.update(cx, |view, cx| {
8406 view.fold_ranges(
8407 vec![
8408 Point::new(0, 2)..Point::new(1, 2),
8409 Point::new(2, 3)..Point::new(4, 1),
8410 Point::new(7, 0)..Point::new(8, 4),
8411 ],
8412 cx,
8413 );
8414 view.change_selections(None, cx, |s| {
8415 s.select_display_ranges([
8416 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8417 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8418 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8419 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
8420 ])
8421 });
8422 assert_eq!(
8423 view.display_text(cx),
8424 "aa…bbb\nccc…eeee\nfffff\nggggg\n…i\njjjjj"
8425 );
8426
8427 view.move_line_up(&MoveLineUp, cx);
8428 assert_eq!(
8429 view.display_text(cx),
8430 "aa…bbb\nccc…eeee\nggggg\n…i\njjjjj\nfffff"
8431 );
8432 assert_eq!(
8433 view.selections.display_ranges(cx),
8434 vec![
8435 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8436 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8437 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
8438 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
8439 ]
8440 );
8441 });
8442
8443 view.update(cx, |view, cx| {
8444 view.move_line_down(&MoveLineDown, cx);
8445 assert_eq!(
8446 view.display_text(cx),
8447 "ccc…eeee\naa…bbb\nfffff\nggggg\n…i\njjjjj"
8448 );
8449 assert_eq!(
8450 view.selections.display_ranges(cx),
8451 vec![
8452 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8453 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8454 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8455 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
8456 ]
8457 );
8458 });
8459
8460 view.update(cx, |view, cx| {
8461 view.move_line_down(&MoveLineDown, cx);
8462 assert_eq!(
8463 view.display_text(cx),
8464 "ccc…eeee\nfffff\naa…bbb\nggggg\n…i\njjjjj"
8465 );
8466 assert_eq!(
8467 view.selections.display_ranges(cx),
8468 vec![
8469 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8470 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8471 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8472 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
8473 ]
8474 );
8475 });
8476
8477 view.update(cx, |view, cx| {
8478 view.move_line_up(&MoveLineUp, cx);
8479 assert_eq!(
8480 view.display_text(cx),
8481 "ccc…eeee\naa…bbb\nggggg\n…i\njjjjj\nfffff"
8482 );
8483 assert_eq!(
8484 view.selections.display_ranges(cx),
8485 vec![
8486 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8487 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8488 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
8489 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
8490 ]
8491 );
8492 });
8493 }
8494
8495 #[gpui::test]
8496 fn test_move_line_up_down_with_blocks(cx: &mut gpui::MutableAppContext) {
8497 cx.set_global(Settings::test(cx));
8498 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
8499 let snapshot = buffer.read(cx).snapshot(cx);
8500 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8501 editor.update(cx, |editor, cx| {
8502 editor.insert_blocks(
8503 [BlockProperties {
8504 style: BlockStyle::Fixed,
8505 position: snapshot.anchor_after(Point::new(2, 0)),
8506 disposition: BlockDisposition::Below,
8507 height: 1,
8508 render: Arc::new(|_| Empty::new().boxed()),
8509 }],
8510 cx,
8511 );
8512 editor.change_selections(None, cx, |s| {
8513 s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
8514 });
8515 editor.move_line_down(&MoveLineDown, cx);
8516 });
8517 }
8518
8519 #[gpui::test]
8520 fn test_transpose(cx: &mut gpui::MutableAppContext) {
8521 cx.set_global(Settings::test(cx));
8522
8523 cx.add_window(Default::default(), |cx| {
8524 let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
8525
8526 editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
8527 editor.transpose(&Default::default(), cx);
8528 assert_eq!(editor.text(cx), "bac");
8529 assert_eq!(editor.selections.ranges(cx), [2..2]);
8530
8531 editor.transpose(&Default::default(), cx);
8532 assert_eq!(editor.text(cx), "bca");
8533 assert_eq!(editor.selections.ranges(cx), [3..3]);
8534
8535 editor.transpose(&Default::default(), cx);
8536 assert_eq!(editor.text(cx), "bac");
8537 assert_eq!(editor.selections.ranges(cx), [3..3]);
8538
8539 editor
8540 })
8541 .1;
8542
8543 cx.add_window(Default::default(), |cx| {
8544 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
8545
8546 editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
8547 editor.transpose(&Default::default(), cx);
8548 assert_eq!(editor.text(cx), "acb\nde");
8549 assert_eq!(editor.selections.ranges(cx), [3..3]);
8550
8551 editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
8552 editor.transpose(&Default::default(), cx);
8553 assert_eq!(editor.text(cx), "acbd\ne");
8554 assert_eq!(editor.selections.ranges(cx), [5..5]);
8555
8556 editor.transpose(&Default::default(), cx);
8557 assert_eq!(editor.text(cx), "acbde\n");
8558 assert_eq!(editor.selections.ranges(cx), [6..6]);
8559
8560 editor.transpose(&Default::default(), cx);
8561 assert_eq!(editor.text(cx), "acbd\ne");
8562 assert_eq!(editor.selections.ranges(cx), [6..6]);
8563
8564 editor
8565 })
8566 .1;
8567
8568 cx.add_window(Default::default(), |cx| {
8569 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
8570
8571 editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
8572 editor.transpose(&Default::default(), cx);
8573 assert_eq!(editor.text(cx), "bacd\ne");
8574 assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
8575
8576 editor.transpose(&Default::default(), cx);
8577 assert_eq!(editor.text(cx), "bcade\n");
8578 assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
8579
8580 editor.transpose(&Default::default(), cx);
8581 assert_eq!(editor.text(cx), "bcda\ne");
8582 assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
8583
8584 editor.transpose(&Default::default(), cx);
8585 assert_eq!(editor.text(cx), "bcade\n");
8586 assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
8587
8588 editor.transpose(&Default::default(), cx);
8589 assert_eq!(editor.text(cx), "bcaed\n");
8590 assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
8591
8592 editor
8593 })
8594 .1;
8595
8596 cx.add_window(Default::default(), |cx| {
8597 let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
8598
8599 editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
8600 editor.transpose(&Default::default(), cx);
8601 assert_eq!(editor.text(cx), "🏀🍐✋");
8602 assert_eq!(editor.selections.ranges(cx), [8..8]);
8603
8604 editor.transpose(&Default::default(), cx);
8605 assert_eq!(editor.text(cx), "🏀✋🍐");
8606 assert_eq!(editor.selections.ranges(cx), [11..11]);
8607
8608 editor.transpose(&Default::default(), cx);
8609 assert_eq!(editor.text(cx), "🏀🍐✋");
8610 assert_eq!(editor.selections.ranges(cx), [11..11]);
8611
8612 editor
8613 })
8614 .1;
8615 }
8616
8617 #[gpui::test]
8618 async fn test_clipboard(cx: &mut gpui::TestAppContext) {
8619 let mut cx = EditorTestContext::new(cx).await;
8620
8621 cx.set_state("[one✅ }two [three }four [five }six ");
8622 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8623 cx.assert_editor_state("|two |four |six ");
8624
8625 // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
8626 cx.set_state("two |four |six |");
8627 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8628 cx.assert_editor_state("two one✅ |four three |six five |");
8629
8630 // Paste again but with only two cursors. Since the number of cursors doesn't
8631 // match the number of slices in the clipboard, the entire clipboard text
8632 // is pasted at each cursor.
8633 cx.set_state("|two one✅ four three six five |");
8634 cx.update_editor(|e, cx| {
8635 e.handle_input("( ", cx);
8636 e.paste(&Paste, cx);
8637 e.handle_input(") ", cx);
8638 });
8639 cx.assert_editor_state(indoc! {"
8640 ( one✅
8641 three
8642 five ) |two one✅ four three six five ( one✅
8643 three
8644 five ) |"});
8645
8646 // Cut with three selections, one of which is full-line.
8647 cx.set_state(indoc! {"
8648 1[2}3
8649 4|567
8650 [8}9"});
8651 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8652 cx.assert_editor_state(indoc! {"
8653 1|3
8654 |9"});
8655
8656 // Paste with three selections, noticing how the copied selection that was full-line
8657 // gets inserted before the second cursor.
8658 cx.set_state(indoc! {"
8659 1|3
8660 9|
8661 [o}ne"});
8662 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8663 cx.assert_editor_state(indoc! {"
8664 12|3
8665 4567
8666 9|
8667 8|ne"});
8668
8669 // Copy with a single cursor only, which writes the whole line into the clipboard.
8670 cx.set_state(indoc! {"
8671 The quick brown
8672 fox ju|mps over
8673 the lazy dog"});
8674 cx.update_editor(|e, cx| e.copy(&Copy, cx));
8675 cx.cx.assert_clipboard_content(Some("fox jumps over\n"));
8676
8677 // Paste with three selections, noticing how the copied full-line selection is inserted
8678 // before the empty selections but replaces the selection that is non-empty.
8679 cx.set_state(indoc! {"
8680 T|he quick brown
8681 [fo}x jumps over
8682 t|he lazy dog"});
8683 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8684 cx.assert_editor_state(indoc! {"
8685 fox jumps over
8686 T|he quick brown
8687 fox jumps over
8688 |x jumps over
8689 fox jumps over
8690 t|he lazy dog"});
8691 }
8692
8693 #[gpui::test]
8694 async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
8695 let mut cx = EditorTestContext::new(cx).await;
8696 let language = Arc::new(Language::new(
8697 LanguageConfig::default(),
8698 Some(tree_sitter_rust::language()),
8699 ));
8700 cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
8701
8702 // Cut an indented block, without the leading whitespace.
8703 cx.set_state(indoc! {"
8704 const a = (
8705 b(),
8706 [c(
8707 d,
8708 e
8709 )}
8710 );
8711 "});
8712 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8713 cx.assert_editor_state(indoc! {"
8714 const a = (
8715 b(),
8716 |
8717 );
8718 "});
8719
8720 // Paste it at the same position.
8721 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8722 cx.assert_editor_state(indoc! {"
8723 const a = (
8724 b(),
8725 c(
8726 d,
8727 e
8728 )|
8729 );
8730 "});
8731
8732 // Paste it at a line with a lower indent level.
8733 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8734 cx.set_state(indoc! {"
8735 |
8736 const a = (
8737 b(),
8738 );
8739 "});
8740 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8741 cx.assert_editor_state(indoc! {"
8742 c(
8743 d,
8744 e
8745 )|
8746 const a = (
8747 b(),
8748 );
8749 "});
8750
8751 // Cut an indented block, with the leading whitespace.
8752 cx.set_state(indoc! {"
8753 const a = (
8754 b(),
8755 [ c(
8756 d,
8757 e
8758 )
8759 });
8760 "});
8761 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8762 cx.assert_editor_state(indoc! {"
8763 const a = (
8764 b(),
8765 |);
8766 "});
8767
8768 // Paste it at the same position.
8769 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8770 cx.assert_editor_state(indoc! {"
8771 const a = (
8772 b(),
8773 c(
8774 d,
8775 e
8776 )
8777 |);
8778 "});
8779
8780 // Paste it at a line with a higher indent level.
8781 cx.set_state(indoc! {"
8782 const a = (
8783 b(),
8784 c(
8785 d,
8786 e|
8787 )
8788 );
8789 "});
8790 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8791 cx.set_state(indoc! {"
8792 const a = (
8793 b(),
8794 c(
8795 d,
8796 ec(
8797 d,
8798 e
8799 )|
8800 )
8801 );
8802 "});
8803 }
8804
8805 #[gpui::test]
8806 fn test_select_all(cx: &mut gpui::MutableAppContext) {
8807 cx.set_global(Settings::test(cx));
8808 let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
8809 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8810 view.update(cx, |view, cx| {
8811 view.select_all(&SelectAll, cx);
8812 assert_eq!(
8813 view.selections.display_ranges(cx),
8814 &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
8815 );
8816 });
8817 }
8818
8819 #[gpui::test]
8820 fn test_select_line(cx: &mut gpui::MutableAppContext) {
8821 cx.set_global(Settings::test(cx));
8822 let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
8823 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8824 view.update(cx, |view, cx| {
8825 view.change_selections(None, cx, |s| {
8826 s.select_display_ranges([
8827 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8828 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8829 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8830 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
8831 ])
8832 });
8833 view.select_line(&SelectLine, cx);
8834 assert_eq!(
8835 view.selections.display_ranges(cx),
8836 vec![
8837 DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
8838 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
8839 ]
8840 );
8841 });
8842
8843 view.update(cx, |view, cx| {
8844 view.select_line(&SelectLine, cx);
8845 assert_eq!(
8846 view.selections.display_ranges(cx),
8847 vec![
8848 DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
8849 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
8850 ]
8851 );
8852 });
8853
8854 view.update(cx, |view, cx| {
8855 view.select_line(&SelectLine, cx);
8856 assert_eq!(
8857 view.selections.display_ranges(cx),
8858 vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
8859 );
8860 });
8861 }
8862
8863 #[gpui::test]
8864 fn test_split_selection_into_lines(cx: &mut gpui::MutableAppContext) {
8865 cx.set_global(Settings::test(cx));
8866 let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
8867 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8868 view.update(cx, |view, cx| {
8869 view.fold_ranges(
8870 vec![
8871 Point::new(0, 2)..Point::new(1, 2),
8872 Point::new(2, 3)..Point::new(4, 1),
8873 Point::new(7, 0)..Point::new(8, 4),
8874 ],
8875 cx,
8876 );
8877 view.change_selections(None, cx, |s| {
8878 s.select_display_ranges([
8879 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8880 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8881 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8882 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
8883 ])
8884 });
8885 assert_eq!(view.display_text(cx), "aa…bbb\nccc…eeee\nfffff\nggggg\n…i");
8886 });
8887
8888 view.update(cx, |view, cx| {
8889 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
8890 assert_eq!(
8891 view.display_text(cx),
8892 "aaaaa\nbbbbb\nccc…eeee\nfffff\nggggg\n…i"
8893 );
8894 assert_eq!(
8895 view.selections.display_ranges(cx),
8896 [
8897 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8898 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8899 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
8900 DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
8901 ]
8902 );
8903 });
8904
8905 view.update(cx, |view, cx| {
8906 view.change_selections(None, cx, |s| {
8907 s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
8908 });
8909 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
8910 assert_eq!(
8911 view.display_text(cx),
8912 "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
8913 );
8914 assert_eq!(
8915 view.selections.display_ranges(cx),
8916 [
8917 DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
8918 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
8919 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
8920 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
8921 DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
8922 DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
8923 DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
8924 DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
8925 ]
8926 );
8927 });
8928 }
8929
8930 #[gpui::test]
8931 fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) {
8932 cx.set_global(Settings::test(cx));
8933 let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
8934 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8935
8936 view.update(cx, |view, cx| {
8937 view.change_selections(None, cx, |s| {
8938 s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)])
8939 });
8940 });
8941 view.update(cx, |view, cx| {
8942 view.add_selection_above(&AddSelectionAbove, cx);
8943 assert_eq!(
8944 view.selections.display_ranges(cx),
8945 vec![
8946 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8947 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8948 ]
8949 );
8950 });
8951
8952 view.update(cx, |view, cx| {
8953 view.add_selection_above(&AddSelectionAbove, cx);
8954 assert_eq!(
8955 view.selections.display_ranges(cx),
8956 vec![
8957 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8958 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8959 ]
8960 );
8961 });
8962
8963 view.update(cx, |view, cx| {
8964 view.add_selection_below(&AddSelectionBelow, cx);
8965 assert_eq!(
8966 view.selections.display_ranges(cx),
8967 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
8968 );
8969
8970 view.undo_selection(&UndoSelection, cx);
8971 assert_eq!(
8972 view.selections.display_ranges(cx),
8973 vec![
8974 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8975 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8976 ]
8977 );
8978
8979 view.redo_selection(&RedoSelection, cx);
8980 assert_eq!(
8981 view.selections.display_ranges(cx),
8982 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
8983 );
8984 });
8985
8986 view.update(cx, |view, cx| {
8987 view.add_selection_below(&AddSelectionBelow, cx);
8988 assert_eq!(
8989 view.selections.display_ranges(cx),
8990 vec![
8991 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
8992 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
8993 ]
8994 );
8995 });
8996
8997 view.update(cx, |view, cx| {
8998 view.add_selection_below(&AddSelectionBelow, cx);
8999 assert_eq!(
9000 view.selections.display_ranges(cx),
9001 vec![
9002 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
9003 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
9004 ]
9005 );
9006 });
9007
9008 view.update(cx, |view, cx| {
9009 view.change_selections(None, cx, |s| {
9010 s.select_display_ranges([DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)])
9011 });
9012 });
9013 view.update(cx, |view, cx| {
9014 view.add_selection_below(&AddSelectionBelow, cx);
9015 assert_eq!(
9016 view.selections.display_ranges(cx),
9017 vec![
9018 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
9019 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
9020 ]
9021 );
9022 });
9023
9024 view.update(cx, |view, cx| {
9025 view.add_selection_below(&AddSelectionBelow, cx);
9026 assert_eq!(
9027 view.selections.display_ranges(cx),
9028 vec![
9029 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
9030 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
9031 ]
9032 );
9033 });
9034
9035 view.update(cx, |view, cx| {
9036 view.add_selection_above(&AddSelectionAbove, cx);
9037 assert_eq!(
9038 view.selections.display_ranges(cx),
9039 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
9040 );
9041 });
9042
9043 view.update(cx, |view, cx| {
9044 view.add_selection_above(&AddSelectionAbove, cx);
9045 assert_eq!(
9046 view.selections.display_ranges(cx),
9047 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
9048 );
9049 });
9050
9051 view.update(cx, |view, cx| {
9052 view.change_selections(None, cx, |s| {
9053 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)])
9054 });
9055 view.add_selection_below(&AddSelectionBelow, cx);
9056 assert_eq!(
9057 view.selections.display_ranges(cx),
9058 vec![
9059 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
9060 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
9061 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
9062 ]
9063 );
9064 });
9065
9066 view.update(cx, |view, cx| {
9067 view.add_selection_below(&AddSelectionBelow, cx);
9068 assert_eq!(
9069 view.selections.display_ranges(cx),
9070 vec![
9071 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
9072 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
9073 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
9074 DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
9075 ]
9076 );
9077 });
9078
9079 view.update(cx, |view, cx| {
9080 view.add_selection_above(&AddSelectionAbove, cx);
9081 assert_eq!(
9082 view.selections.display_ranges(cx),
9083 vec![
9084 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
9085 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
9086 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
9087 ]
9088 );
9089 });
9090
9091 view.update(cx, |view, cx| {
9092 view.change_selections(None, cx, |s| {
9093 s.select_display_ranges([DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)])
9094 });
9095 });
9096 view.update(cx, |view, cx| {
9097 view.add_selection_above(&AddSelectionAbove, cx);
9098 assert_eq!(
9099 view.selections.display_ranges(cx),
9100 vec![
9101 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
9102 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
9103 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
9104 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
9105 ]
9106 );
9107 });
9108
9109 view.update(cx, |view, cx| {
9110 view.add_selection_below(&AddSelectionBelow, cx);
9111 assert_eq!(
9112 view.selections.display_ranges(cx),
9113 vec![
9114 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
9115 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
9116 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
9117 ]
9118 );
9119 });
9120 }
9121
9122 #[gpui::test]
9123 fn test_select_next(cx: &mut gpui::MutableAppContext) {
9124 cx.set_global(Settings::test(cx));
9125
9126 let (text, ranges) = marked_text_ranges("[abc]\n[abc] [abc]\ndefabc\n[abc]");
9127 let buffer = MultiBuffer::build_simple(&text, cx);
9128 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
9129
9130 view.update(cx, |view, cx| {
9131 view.change_selections(None, cx, |s| {
9132 s.select_ranges([ranges[1].start + 1..ranges[1].start + 1])
9133 });
9134 view.select_next(
9135 &SelectNext {
9136 replace_newest: false,
9137 },
9138 cx,
9139 );
9140 assert_eq!(view.selections.ranges(cx), &ranges[1..2]);
9141
9142 view.select_next(
9143 &SelectNext {
9144 replace_newest: false,
9145 },
9146 cx,
9147 );
9148 assert_eq!(view.selections.ranges(cx), &ranges[1..3]);
9149
9150 view.undo_selection(&UndoSelection, cx);
9151 assert_eq!(view.selections.ranges(cx), &ranges[1..2]);
9152
9153 view.redo_selection(&RedoSelection, cx);
9154 assert_eq!(view.selections.ranges(cx), &ranges[1..3]);
9155
9156 view.select_next(
9157 &SelectNext {
9158 replace_newest: false,
9159 },
9160 cx,
9161 );
9162 assert_eq!(view.selections.ranges(cx), &ranges[1..4]);
9163
9164 view.select_next(
9165 &SelectNext {
9166 replace_newest: false,
9167 },
9168 cx,
9169 );
9170 assert_eq!(view.selections.ranges(cx), &ranges[0..4]);
9171 });
9172 }
9173
9174 #[gpui::test]
9175 async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
9176 cx.update(|cx| cx.set_global(Settings::test(cx)));
9177 let language = Arc::new(Language::new(
9178 LanguageConfig::default(),
9179 Some(tree_sitter_rust::language()),
9180 ));
9181
9182 let text = r#"
9183 use mod1::mod2::{mod3, mod4};
9184
9185 fn fn_1(param1: bool, param2: &str) {
9186 let var1 = "text";
9187 }
9188 "#
9189 .unindent();
9190
9191 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9192 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9193 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9194 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9195 .await;
9196
9197 view.update(cx, |view, cx| {
9198 view.change_selections(None, cx, |s| {
9199 s.select_display_ranges([
9200 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
9201 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
9202 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
9203 ]);
9204 });
9205 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9206 });
9207 assert_eq!(
9208 view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
9209 &[
9210 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
9211 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
9212 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
9213 ]
9214 );
9215
9216 view.update(cx, |view, cx| {
9217 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9218 });
9219 assert_eq!(
9220 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9221 &[
9222 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
9223 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
9224 ]
9225 );
9226
9227 view.update(cx, |view, cx| {
9228 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9229 });
9230 assert_eq!(
9231 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9232 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
9233 );
9234
9235 // Trying to expand the selected syntax node one more time has no effect.
9236 view.update(cx, |view, cx| {
9237 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9238 });
9239 assert_eq!(
9240 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9241 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
9242 );
9243
9244 view.update(cx, |view, cx| {
9245 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
9246 });
9247 assert_eq!(
9248 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9249 &[
9250 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
9251 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
9252 ]
9253 );
9254
9255 view.update(cx, |view, cx| {
9256 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
9257 });
9258 assert_eq!(
9259 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9260 &[
9261 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
9262 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
9263 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
9264 ]
9265 );
9266
9267 view.update(cx, |view, cx| {
9268 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
9269 });
9270 assert_eq!(
9271 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9272 &[
9273 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
9274 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
9275 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
9276 ]
9277 );
9278
9279 // Trying to shrink the selected syntax node one more time has no effect.
9280 view.update(cx, |view, cx| {
9281 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
9282 });
9283 assert_eq!(
9284 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9285 &[
9286 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
9287 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
9288 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
9289 ]
9290 );
9291
9292 // Ensure that we keep expanding the selection if the larger selection starts or ends within
9293 // a fold.
9294 view.update(cx, |view, cx| {
9295 view.fold_ranges(
9296 vec![
9297 Point::new(0, 21)..Point::new(0, 24),
9298 Point::new(3, 20)..Point::new(3, 22),
9299 ],
9300 cx,
9301 );
9302 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9303 });
9304 assert_eq!(
9305 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9306 &[
9307 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
9308 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
9309 DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
9310 ]
9311 );
9312 }
9313
9314 #[gpui::test]
9315 async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
9316 cx.update(|cx| cx.set_global(Settings::test(cx)));
9317 let language = Arc::new(
9318 Language::new(
9319 LanguageConfig {
9320 brackets: vec![
9321 BracketPair {
9322 start: "{".to_string(),
9323 end: "}".to_string(),
9324 close: false,
9325 newline: true,
9326 },
9327 BracketPair {
9328 start: "(".to_string(),
9329 end: ")".to_string(),
9330 close: false,
9331 newline: true,
9332 },
9333 ],
9334 ..Default::default()
9335 },
9336 Some(tree_sitter_rust::language()),
9337 )
9338 .with_indents_query(
9339 r#"
9340 (_ "(" ")" @end) @indent
9341 (_ "{" "}" @end) @indent
9342 "#,
9343 )
9344 .unwrap(),
9345 );
9346
9347 let text = "fn a() {}";
9348
9349 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9350 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9351 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9352 editor
9353 .condition(&cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
9354 .await;
9355
9356 editor.update(cx, |editor, cx| {
9357 editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
9358 editor.newline(&Newline, cx);
9359 assert_eq!(editor.text(cx), "fn a(\n \n) {\n \n}\n");
9360 assert_eq!(
9361 editor.selections.ranges(cx),
9362 &[
9363 Point::new(1, 4)..Point::new(1, 4),
9364 Point::new(3, 4)..Point::new(3, 4),
9365 Point::new(5, 0)..Point::new(5, 0)
9366 ]
9367 );
9368 });
9369 }
9370
9371 #[gpui::test]
9372 async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
9373 cx.update(|cx| cx.set_global(Settings::test(cx)));
9374 let language = Arc::new(Language::new(
9375 LanguageConfig {
9376 brackets: vec![
9377 BracketPair {
9378 start: "{".to_string(),
9379 end: "}".to_string(),
9380 close: true,
9381 newline: true,
9382 },
9383 BracketPair {
9384 start: "/*".to_string(),
9385 end: " */".to_string(),
9386 close: true,
9387 newline: true,
9388 },
9389 BracketPair {
9390 start: "[".to_string(),
9391 end: "]".to_string(),
9392 close: false,
9393 newline: true,
9394 },
9395 ],
9396 autoclose_before: "})]".to_string(),
9397 ..Default::default()
9398 },
9399 Some(tree_sitter_rust::language()),
9400 ));
9401
9402 let text = r#"
9403 a
9404
9405 /
9406
9407 "#
9408 .unindent();
9409
9410 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9411 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9412 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9413 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9414 .await;
9415
9416 view.update(cx, |view, cx| {
9417 view.change_selections(None, cx, |s| {
9418 s.select_display_ranges([
9419 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9420 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
9421 ])
9422 });
9423
9424 view.handle_input("{", cx);
9425 view.handle_input("{", cx);
9426 view.handle_input("{", cx);
9427 assert_eq!(
9428 view.text(cx),
9429 "
9430 {{{}}}
9431 {{{}}}
9432 /
9433
9434 "
9435 .unindent()
9436 );
9437
9438 view.move_right(&MoveRight, cx);
9439 view.handle_input("}", cx);
9440 view.handle_input("}", cx);
9441 view.handle_input("}", cx);
9442 assert_eq!(
9443 view.text(cx),
9444 "
9445 {{{}}}}
9446 {{{}}}}
9447 /
9448
9449 "
9450 .unindent()
9451 );
9452
9453 view.undo(&Undo, cx);
9454 view.handle_input("/", cx);
9455 view.handle_input("*", cx);
9456 assert_eq!(
9457 view.text(cx),
9458 "
9459 /* */
9460 /* */
9461 /
9462
9463 "
9464 .unindent()
9465 );
9466
9467 view.undo(&Undo, cx);
9468 view.change_selections(None, cx, |s| {
9469 s.select_display_ranges([
9470 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
9471 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
9472 ])
9473 });
9474 view.handle_input("*", cx);
9475 assert_eq!(
9476 view.text(cx),
9477 "
9478 a
9479
9480 /*
9481 *
9482 "
9483 .unindent()
9484 );
9485
9486 // Don't autoclose if the next character isn't whitespace and isn't
9487 // listed in the language's "autoclose_before" section.
9488 view.finalize_last_transaction(cx);
9489 view.change_selections(None, cx, |s| {
9490 s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)])
9491 });
9492 view.handle_input("{", cx);
9493 assert_eq!(
9494 view.text(cx),
9495 "
9496 {a
9497
9498 /*
9499 *
9500 "
9501 .unindent()
9502 );
9503
9504 view.undo(&Undo, cx);
9505 view.change_selections(None, cx, |s| {
9506 s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1)])
9507 });
9508 view.handle_input("{", cx);
9509 assert_eq!(
9510 view.text(cx),
9511 "
9512 {a}
9513
9514 /*
9515 *
9516 "
9517 .unindent()
9518 );
9519 assert_eq!(
9520 view.selections.display_ranges(cx),
9521 [DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]
9522 );
9523
9524 view.undo(&Undo, cx);
9525 view.handle_input("[", cx);
9526 assert_eq!(
9527 view.text(cx),
9528 "
9529 [a]
9530
9531 /*
9532 *
9533 "
9534 .unindent()
9535 );
9536 assert_eq!(
9537 view.selections.display_ranges(cx),
9538 [DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]
9539 );
9540
9541 view.undo(&Undo, cx);
9542 view.change_selections(None, cx, |s| {
9543 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)])
9544 });
9545 view.handle_input("[", cx);
9546 assert_eq!(
9547 view.text(cx),
9548 "
9549 a[
9550
9551 /*
9552 *
9553 "
9554 .unindent()
9555 );
9556 assert_eq!(
9557 view.selections.display_ranges(cx),
9558 [DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2)]
9559 );
9560 });
9561 }
9562
9563 #[gpui::test]
9564 async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
9565 cx.update(|cx| cx.set_global(Settings::test(cx)));
9566 let language = Arc::new(Language::new(
9567 LanguageConfig {
9568 brackets: vec![BracketPair {
9569 start: "{".to_string(),
9570 end: "}".to_string(),
9571 close: true,
9572 newline: true,
9573 }],
9574 ..Default::default()
9575 },
9576 Some(tree_sitter_rust::language()),
9577 ));
9578
9579 let text = r#"
9580 a
9581 b
9582 c
9583 "#
9584 .unindent();
9585
9586 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9587 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9588 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9589 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9590 .await;
9591
9592 view.update(cx, |view, cx| {
9593 view.change_selections(None, cx, |s| {
9594 s.select_display_ranges([
9595 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9596 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
9597 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
9598 ])
9599 });
9600
9601 view.handle_input("{", cx);
9602 view.handle_input("{", cx);
9603 view.handle_input("{", cx);
9604 assert_eq!(
9605 view.text(cx),
9606 "
9607 {{{a}}}
9608 {{{b}}}
9609 {{{c}}}
9610 "
9611 .unindent()
9612 );
9613 assert_eq!(
9614 view.selections.display_ranges(cx),
9615 [
9616 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
9617 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
9618 DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
9619 ]
9620 );
9621
9622 view.undo(&Undo, cx);
9623 assert_eq!(
9624 view.text(cx),
9625 "
9626 a
9627 b
9628 c
9629 "
9630 .unindent()
9631 );
9632 assert_eq!(
9633 view.selections.display_ranges(cx),
9634 [
9635 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9636 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
9637 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
9638 ]
9639 );
9640 });
9641 }
9642
9643 #[gpui::test]
9644 async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
9645 cx.update(|cx| cx.set_global(Settings::test(cx)));
9646 let language = Arc::new(Language::new(
9647 LanguageConfig {
9648 brackets: vec![BracketPair {
9649 start: "{".to_string(),
9650 end: "}".to_string(),
9651 close: true,
9652 newline: true,
9653 }],
9654 autoclose_before: "}".to_string(),
9655 ..Default::default()
9656 },
9657 Some(tree_sitter_rust::language()),
9658 ));
9659
9660 let text = r#"
9661 a
9662 b
9663 c
9664 "#
9665 .unindent();
9666
9667 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9668 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9669 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9670 editor
9671 .condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9672 .await;
9673
9674 editor.update(cx, |editor, cx| {
9675 editor.change_selections(None, cx, |s| {
9676 s.select_ranges([
9677 Point::new(0, 1)..Point::new(0, 1),
9678 Point::new(1, 1)..Point::new(1, 1),
9679 Point::new(2, 1)..Point::new(2, 1),
9680 ])
9681 });
9682
9683 editor.handle_input("{", cx);
9684 editor.handle_input("{", cx);
9685 editor.handle_input("_", cx);
9686 assert_eq!(
9687 editor.text(cx),
9688 "
9689 a{{_}}
9690 b{{_}}
9691 c{{_}}
9692 "
9693 .unindent()
9694 );
9695 assert_eq!(
9696 editor.selections.ranges::<Point>(cx),
9697 [
9698 Point::new(0, 4)..Point::new(0, 4),
9699 Point::new(1, 4)..Point::new(1, 4),
9700 Point::new(2, 4)..Point::new(2, 4)
9701 ]
9702 );
9703
9704 editor.backspace(&Default::default(), cx);
9705 editor.backspace(&Default::default(), cx);
9706 assert_eq!(
9707 editor.text(cx),
9708 "
9709 a{}
9710 b{}
9711 c{}
9712 "
9713 .unindent()
9714 );
9715 assert_eq!(
9716 editor.selections.ranges::<Point>(cx),
9717 [
9718 Point::new(0, 2)..Point::new(0, 2),
9719 Point::new(1, 2)..Point::new(1, 2),
9720 Point::new(2, 2)..Point::new(2, 2)
9721 ]
9722 );
9723
9724 editor.delete_to_previous_word_start(&Default::default(), cx);
9725 assert_eq!(
9726 editor.text(cx),
9727 "
9728 a
9729 b
9730 c
9731 "
9732 .unindent()
9733 );
9734 assert_eq!(
9735 editor.selections.ranges::<Point>(cx),
9736 [
9737 Point::new(0, 1)..Point::new(0, 1),
9738 Point::new(1, 1)..Point::new(1, 1),
9739 Point::new(2, 1)..Point::new(2, 1)
9740 ]
9741 );
9742 });
9743 }
9744
9745 #[gpui::test]
9746 async fn test_snippets(cx: &mut gpui::TestAppContext) {
9747 cx.update(|cx| cx.set_global(Settings::test(cx)));
9748
9749 let (text, insertion_ranges) = marked_text_ranges(indoc! {"
9750 a.| b
9751 a.| b
9752 a.| b"});
9753 let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
9754 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9755
9756 editor.update(cx, |editor, cx| {
9757 let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
9758
9759 editor
9760 .insert_snippet(&insertion_ranges, snippet, cx)
9761 .unwrap();
9762
9763 fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text_ranges: &str) {
9764 let range_markers = ('<', '>');
9765 let (expected_text, mut selection_ranges_lookup) =
9766 marked_text_ranges_by(marked_text_ranges, vec![range_markers.clone().into()]);
9767 let selection_ranges = selection_ranges_lookup
9768 .remove(&range_markers.into())
9769 .unwrap();
9770 assert_eq!(editor.text(cx), expected_text);
9771 assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
9772 }
9773 assert(
9774 editor,
9775 cx,
9776 indoc! {"
9777 a.f(<one>, two, <three>) b
9778 a.f(<one>, two, <three>) b
9779 a.f(<one>, two, <three>) b"},
9780 );
9781
9782 // Can't move earlier than the first tab stop
9783 assert!(!editor.move_to_prev_snippet_tabstop(cx));
9784 assert(
9785 editor,
9786 cx,
9787 indoc! {"
9788 a.f(<one>, two, <three>) b
9789 a.f(<one>, two, <three>) b
9790 a.f(<one>, two, <three>) b"},
9791 );
9792
9793 assert!(editor.move_to_next_snippet_tabstop(cx));
9794 assert(
9795 editor,
9796 cx,
9797 indoc! {"
9798 a.f(one, <two>, three) b
9799 a.f(one, <two>, three) b
9800 a.f(one, <two>, three) b"},
9801 );
9802
9803 editor.move_to_prev_snippet_tabstop(cx);
9804 assert(
9805 editor,
9806 cx,
9807 indoc! {"
9808 a.f(<one>, two, <three>) b
9809 a.f(<one>, two, <three>) b
9810 a.f(<one>, two, <three>) b"},
9811 );
9812
9813 assert!(editor.move_to_next_snippet_tabstop(cx));
9814 assert(
9815 editor,
9816 cx,
9817 indoc! {"
9818 a.f(one, <two>, three) b
9819 a.f(one, <two>, three) b
9820 a.f(one, <two>, three) b"},
9821 );
9822 assert!(editor.move_to_next_snippet_tabstop(cx));
9823 assert(
9824 editor,
9825 cx,
9826 indoc! {"
9827 a.f(one, two, three)<> b
9828 a.f(one, two, three)<> b
9829 a.f(one, two, three)<> b"},
9830 );
9831
9832 // As soon as the last tab stop is reached, snippet state is gone
9833 editor.move_to_prev_snippet_tabstop(cx);
9834 assert(
9835 editor,
9836 cx,
9837 indoc! {"
9838 a.f(one, two, three)<> b
9839 a.f(one, two, three)<> b
9840 a.f(one, two, three)<> b"},
9841 );
9842 });
9843 }
9844
9845 #[gpui::test]
9846 async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
9847 cx.foreground().forbid_parking();
9848
9849 let mut language = Language::new(
9850 LanguageConfig {
9851 name: "Rust".into(),
9852 path_suffixes: vec!["rs".to_string()],
9853 ..Default::default()
9854 },
9855 Some(tree_sitter_rust::language()),
9856 );
9857 let mut fake_servers = language
9858 .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
9859 capabilities: lsp::ServerCapabilities {
9860 document_formatting_provider: Some(lsp::OneOf::Left(true)),
9861 ..Default::default()
9862 },
9863 ..Default::default()
9864 }))
9865 .await;
9866
9867 let fs = FakeFs::new(cx.background().clone());
9868 fs.insert_file("/file.rs", Default::default()).await;
9869
9870 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
9871 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
9872 let buffer = project
9873 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
9874 .await
9875 .unwrap();
9876
9877 cx.foreground().start_waiting();
9878 let fake_server = fake_servers.next().await.unwrap();
9879
9880 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9881 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9882 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9883 assert!(cx.read(|cx| editor.is_dirty(cx)));
9884
9885 let save = cx.update(|cx| editor.save(project.clone(), cx));
9886 fake_server
9887 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9888 assert_eq!(
9889 params.text_document.uri,
9890 lsp::Url::from_file_path("/file.rs").unwrap()
9891 );
9892 assert_eq!(params.options.tab_size, 4);
9893 Ok(Some(vec![lsp::TextEdit::new(
9894 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
9895 ", ".to_string(),
9896 )]))
9897 })
9898 .next()
9899 .await;
9900 cx.foreground().start_waiting();
9901 save.await.unwrap();
9902 assert_eq!(
9903 editor.read_with(cx, |editor, cx| editor.text(cx)),
9904 "one, two\nthree\n"
9905 );
9906 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9907
9908 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9909 assert!(cx.read(|cx| editor.is_dirty(cx)));
9910
9911 // Ensure we can still save even if formatting hangs.
9912 fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9913 assert_eq!(
9914 params.text_document.uri,
9915 lsp::Url::from_file_path("/file.rs").unwrap()
9916 );
9917 futures::future::pending::<()>().await;
9918 unreachable!()
9919 });
9920 let save = cx.update(|cx| editor.save(project.clone(), cx));
9921 cx.foreground().advance_clock(items::FORMAT_TIMEOUT);
9922 cx.foreground().start_waiting();
9923 save.await.unwrap();
9924 assert_eq!(
9925 editor.read_with(cx, |editor, cx| editor.text(cx)),
9926 "one\ntwo\nthree\n"
9927 );
9928 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9929
9930 // Set rust language override and assert overriden tabsize is sent to language server
9931 cx.update(|cx| {
9932 cx.update_global::<Settings, _, _>(|settings, _| {
9933 settings.language_overrides.insert(
9934 "Rust".into(),
9935 EditorSettings {
9936 tab_size: Some(8.try_into().unwrap()),
9937 ..Default::default()
9938 },
9939 );
9940 })
9941 });
9942
9943 let save = cx.update(|cx| editor.save(project.clone(), cx));
9944 fake_server
9945 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9946 assert_eq!(
9947 params.text_document.uri,
9948 lsp::Url::from_file_path("/file.rs").unwrap()
9949 );
9950 assert_eq!(params.options.tab_size, 8);
9951 Ok(Some(vec![]))
9952 })
9953 .next()
9954 .await;
9955 cx.foreground().start_waiting();
9956 save.await.unwrap();
9957 }
9958
9959 #[gpui::test]
9960 async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
9961 cx.foreground().forbid_parking();
9962
9963 let mut language = Language::new(
9964 LanguageConfig {
9965 name: "Rust".into(),
9966 path_suffixes: vec!["rs".to_string()],
9967 ..Default::default()
9968 },
9969 Some(tree_sitter_rust::language()),
9970 );
9971 let mut fake_servers = language
9972 .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
9973 capabilities: lsp::ServerCapabilities {
9974 document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
9975 ..Default::default()
9976 },
9977 ..Default::default()
9978 }))
9979 .await;
9980
9981 let fs = FakeFs::new(cx.background().clone());
9982 fs.insert_file("/file.rs", Default::default()).await;
9983
9984 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
9985 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
9986 let buffer = project
9987 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
9988 .await
9989 .unwrap();
9990
9991 cx.foreground().start_waiting();
9992 let fake_server = fake_servers.next().await.unwrap();
9993
9994 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9995 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9996 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9997 assert!(cx.read(|cx| editor.is_dirty(cx)));
9998
9999 let save = cx.update(|cx| editor.save(project.clone(), cx));
10000 fake_server
10001 .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
10002 assert_eq!(
10003 params.text_document.uri,
10004 lsp::Url::from_file_path("/file.rs").unwrap()
10005 );
10006 assert_eq!(params.options.tab_size, 4);
10007 Ok(Some(vec![lsp::TextEdit::new(
10008 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
10009 ", ".to_string(),
10010 )]))
10011 })
10012 .next()
10013 .await;
10014 cx.foreground().start_waiting();
10015 save.await.unwrap();
10016 assert_eq!(
10017 editor.read_with(cx, |editor, cx| editor.text(cx)),
10018 "one, two\nthree\n"
10019 );
10020 assert!(!cx.read(|cx| editor.is_dirty(cx)));
10021
10022 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
10023 assert!(cx.read(|cx| editor.is_dirty(cx)));
10024
10025 // Ensure we can still save even if formatting hangs.
10026 fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
10027 move |params, _| async move {
10028 assert_eq!(
10029 params.text_document.uri,
10030 lsp::Url::from_file_path("/file.rs").unwrap()
10031 );
10032 futures::future::pending::<()>().await;
10033 unreachable!()
10034 },
10035 );
10036 let save = cx.update(|cx| editor.save(project.clone(), cx));
10037 cx.foreground().advance_clock(items::FORMAT_TIMEOUT);
10038 cx.foreground().start_waiting();
10039 save.await.unwrap();
10040 assert_eq!(
10041 editor.read_with(cx, |editor, cx| editor.text(cx)),
10042 "one\ntwo\nthree\n"
10043 );
10044 assert!(!cx.read(|cx| editor.is_dirty(cx)));
10045
10046 // Set rust language override and assert overriden tabsize is sent to language server
10047 cx.update(|cx| {
10048 cx.update_global::<Settings, _, _>(|settings, _| {
10049 settings.language_overrides.insert(
10050 "Rust".into(),
10051 EditorSettings {
10052 tab_size: Some(8.try_into().unwrap()),
10053 ..Default::default()
10054 },
10055 );
10056 })
10057 });
10058
10059 let save = cx.update(|cx| editor.save(project.clone(), cx));
10060 fake_server
10061 .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
10062 assert_eq!(
10063 params.text_document.uri,
10064 lsp::Url::from_file_path("/file.rs").unwrap()
10065 );
10066 assert_eq!(params.options.tab_size, 8);
10067 Ok(Some(vec![]))
10068 })
10069 .next()
10070 .await;
10071 cx.foreground().start_waiting();
10072 save.await.unwrap();
10073 }
10074
10075 #[gpui::test]
10076 async fn test_completion(cx: &mut gpui::TestAppContext) {
10077 let mut cx = EditorLspTestContext::new_rust(
10078 lsp::ServerCapabilities {
10079 completion_provider: Some(lsp::CompletionOptions {
10080 trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10081 ..Default::default()
10082 }),
10083 ..Default::default()
10084 },
10085 cx,
10086 )
10087 .await;
10088
10089 cx.set_state(indoc! {"
10090 one|
10091 two
10092 three"});
10093 cx.simulate_keystroke(".");
10094 handle_completion_request(
10095 &mut cx,
10096 indoc! {"
10097 one.|<>
10098 two
10099 three"},
10100 vec!["first_completion", "second_completion"],
10101 )
10102 .await;
10103 cx.condition(|editor, _| editor.context_menu_visible())
10104 .await;
10105 let apply_additional_edits = cx.update_editor(|editor, cx| {
10106 editor.move_down(&MoveDown, cx);
10107 editor
10108 .confirm_completion(&ConfirmCompletion::default(), cx)
10109 .unwrap()
10110 });
10111 cx.assert_editor_state(indoc! {"
10112 one.second_completion|
10113 two
10114 three"});
10115
10116 handle_resolve_completion_request(
10117 &mut cx,
10118 Some((
10119 indoc! {"
10120 one.second_completion
10121 two
10122 three<>"},
10123 "\nadditional edit",
10124 )),
10125 )
10126 .await;
10127 apply_additional_edits.await.unwrap();
10128 cx.assert_editor_state(indoc! {"
10129 one.second_completion|
10130 two
10131 three
10132 additional edit"});
10133
10134 cx.set_state(indoc! {"
10135 one.second_completion
10136 two|
10137 three|
10138 additional edit"});
10139 cx.simulate_keystroke(" ");
10140 assert!(cx.editor(|e, _| e.context_menu.is_none()));
10141 cx.simulate_keystroke("s");
10142 assert!(cx.editor(|e, _| e.context_menu.is_none()));
10143
10144 cx.assert_editor_state(indoc! {"
10145 one.second_completion
10146 two s|
10147 three s|
10148 additional edit"});
10149 handle_completion_request(
10150 &mut cx,
10151 indoc! {"
10152 one.second_completion
10153 two s
10154 three <s|>
10155 additional edit"},
10156 vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10157 )
10158 .await;
10159 cx.condition(|editor, _| editor.context_menu_visible())
10160 .await;
10161
10162 cx.simulate_keystroke("i");
10163
10164 handle_completion_request(
10165 &mut cx,
10166 indoc! {"
10167 one.second_completion
10168 two si
10169 three <si|>
10170 additional edit"},
10171 vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10172 )
10173 .await;
10174 cx.condition(|editor, _| editor.context_menu_visible())
10175 .await;
10176
10177 let apply_additional_edits = cx.update_editor(|editor, cx| {
10178 editor
10179 .confirm_completion(&ConfirmCompletion::default(), cx)
10180 .unwrap()
10181 });
10182 cx.assert_editor_state(indoc! {"
10183 one.second_completion
10184 two sixth_completion|
10185 three sixth_completion|
10186 additional edit"});
10187
10188 handle_resolve_completion_request(&mut cx, None).await;
10189 apply_additional_edits.await.unwrap();
10190
10191 cx.update(|cx| {
10192 cx.update_global::<Settings, _, _>(|settings, _| {
10193 settings.show_completions_on_input = false;
10194 })
10195 });
10196 cx.set_state("editor|");
10197 cx.simulate_keystroke(".");
10198 assert!(cx.editor(|e, _| e.context_menu.is_none()));
10199 cx.simulate_keystroke("c");
10200 cx.simulate_keystroke("l");
10201 cx.simulate_keystroke("o");
10202 cx.assert_editor_state("editor.clo|");
10203 assert!(cx.editor(|e, _| e.context_menu.is_none()));
10204 cx.update_editor(|editor, cx| {
10205 editor.show_completions(&ShowCompletions, cx);
10206 });
10207 handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
10208 cx.condition(|editor, _| editor.context_menu_visible())
10209 .await;
10210 let apply_additional_edits = cx.update_editor(|editor, cx| {
10211 editor
10212 .confirm_completion(&ConfirmCompletion::default(), cx)
10213 .unwrap()
10214 });
10215 cx.assert_editor_state("editor.close|");
10216 handle_resolve_completion_request(&mut cx, None).await;
10217 apply_additional_edits.await.unwrap();
10218
10219 // Handle completion request passing a marked string specifying where the completion
10220 // should be triggered from using '|' character, what range should be replaced, and what completions
10221 // should be returned using '<' and '>' to delimit the range
10222 async fn handle_completion_request<'a>(
10223 cx: &mut EditorLspTestContext<'a>,
10224 marked_string: &str,
10225 completions: Vec<&'static str>,
10226 ) {
10227 let complete_from_marker: TextRangeMarker = '|'.into();
10228 let replace_range_marker: TextRangeMarker = ('<', '>').into();
10229 let (_, mut marked_ranges) = marked_text_ranges_by(
10230 marked_string,
10231 vec![complete_from_marker.clone(), replace_range_marker.clone()],
10232 );
10233
10234 let complete_from_position =
10235 cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
10236 let replace_range =
10237 cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
10238
10239 cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
10240 let completions = completions.clone();
10241 async move {
10242 assert_eq!(params.text_document_position.text_document.uri, url.clone());
10243 assert_eq!(
10244 params.text_document_position.position,
10245 complete_from_position
10246 );
10247 Ok(Some(lsp::CompletionResponse::Array(
10248 completions
10249 .iter()
10250 .map(|completion_text| lsp::CompletionItem {
10251 label: completion_text.to_string(),
10252 text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10253 range: replace_range.clone(),
10254 new_text: completion_text.to_string(),
10255 })),
10256 ..Default::default()
10257 })
10258 .collect(),
10259 )))
10260 }
10261 })
10262 .next()
10263 .await;
10264 }
10265
10266 async fn handle_resolve_completion_request<'a>(
10267 cx: &mut EditorLspTestContext<'a>,
10268 edit: Option<(&'static str, &'static str)>,
10269 ) {
10270 let edit = edit.map(|(marked_string, new_text)| {
10271 let replace_range_marker: TextRangeMarker = ('<', '>').into();
10272 let (_, mut marked_ranges) =
10273 marked_text_ranges_by(marked_string, vec![replace_range_marker.clone()]);
10274
10275 let replace_range = cx
10276 .to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
10277
10278 vec![lsp::TextEdit::new(replace_range, new_text.to_string())]
10279 });
10280
10281 cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
10282 let edit = edit.clone();
10283 async move {
10284 Ok(lsp::CompletionItem {
10285 additional_text_edits: edit,
10286 ..Default::default()
10287 })
10288 }
10289 })
10290 .next()
10291 .await;
10292 }
10293 }
10294
10295 #[gpui::test]
10296 async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
10297 cx.update(|cx| cx.set_global(Settings::test(cx)));
10298 let language = Arc::new(Language::new(
10299 LanguageConfig {
10300 line_comment: Some("// ".to_string()),
10301 ..Default::default()
10302 },
10303 Some(tree_sitter_rust::language()),
10304 ));
10305
10306 let text = "
10307 fn a() {
10308 //b();
10309 // c();
10310 // d();
10311 }
10312 "
10313 .unindent();
10314
10315 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
10316 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10317 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
10318
10319 view.update(cx, |editor, cx| {
10320 // If multiple selections intersect a line, the line is only
10321 // toggled once.
10322 editor.change_selections(None, cx, |s| {
10323 s.select_display_ranges([
10324 DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3),
10325 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6),
10326 ])
10327 });
10328 editor.toggle_comments(&ToggleComments, cx);
10329 assert_eq!(
10330 editor.text(cx),
10331 "
10332 fn a() {
10333 b();
10334 c();
10335 d();
10336 }
10337 "
10338 .unindent()
10339 );
10340
10341 // The comment prefix is inserted at the same column for every line
10342 // in a selection.
10343 editor.change_selections(None, cx, |s| {
10344 s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)])
10345 });
10346 editor.toggle_comments(&ToggleComments, cx);
10347 assert_eq!(
10348 editor.text(cx),
10349 "
10350 fn a() {
10351 // b();
10352 // c();
10353 // d();
10354 }
10355 "
10356 .unindent()
10357 );
10358
10359 // If a selection ends at the beginning of a line, that line is not toggled.
10360 editor.change_selections(None, cx, |s| {
10361 s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)])
10362 });
10363 editor.toggle_comments(&ToggleComments, cx);
10364 assert_eq!(
10365 editor.text(cx),
10366 "
10367 fn a() {
10368 // b();
10369 c();
10370 // d();
10371 }
10372 "
10373 .unindent()
10374 );
10375 });
10376 }
10377
10378 #[gpui::test]
10379 fn test_editing_disjoint_excerpts(cx: &mut gpui::MutableAppContext) {
10380 cx.set_global(Settings::test(cx));
10381 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
10382 let multibuffer = cx.add_model(|cx| {
10383 let mut multibuffer = MultiBuffer::new(0);
10384 multibuffer.push_excerpts(
10385 buffer.clone(),
10386 [
10387 ExcerptRange {
10388 context: Point::new(0, 0)..Point::new(0, 4),
10389 primary: None,
10390 },
10391 ExcerptRange {
10392 context: Point::new(1, 0)..Point::new(1, 4),
10393 primary: None,
10394 },
10395 ],
10396 cx,
10397 );
10398 multibuffer
10399 });
10400
10401 assert_eq!(multibuffer.read(cx).read(cx).text(), "aaaa\nbbbb");
10402
10403 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
10404 view.update(cx, |view, cx| {
10405 assert_eq!(view.text(cx), "aaaa\nbbbb");
10406 view.change_selections(None, cx, |s| {
10407 s.select_ranges([
10408 Point::new(0, 0)..Point::new(0, 0),
10409 Point::new(1, 0)..Point::new(1, 0),
10410 ])
10411 });
10412
10413 view.handle_input("X", cx);
10414 assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
10415 assert_eq!(
10416 view.selections.ranges(cx),
10417 [
10418 Point::new(0, 1)..Point::new(0, 1),
10419 Point::new(1, 1)..Point::new(1, 1),
10420 ]
10421 )
10422 });
10423 }
10424
10425 #[gpui::test]
10426 fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) {
10427 cx.set_global(Settings::test(cx));
10428 let (initial_text, excerpt_ranges) = marked_text_ranges(indoc! {"
10429 [aaaa
10430 (bbbb]
10431 cccc)"});
10432 let excerpt_ranges = excerpt_ranges.into_iter().map(|context| ExcerptRange {
10433 context,
10434 primary: None,
10435 });
10436 let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx));
10437 let multibuffer = cx.add_model(|cx| {
10438 let mut multibuffer = MultiBuffer::new(0);
10439 multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
10440 multibuffer
10441 });
10442
10443 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
10444 view.update(cx, |view, cx| {
10445 let (expected_text, selection_ranges) = marked_text_ranges(indoc! {"
10446 aaaa
10447 b|bbb
10448 b|bb|b
10449 cccc"});
10450 assert_eq!(view.text(cx), expected_text);
10451 view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
10452
10453 view.handle_input("X", cx);
10454
10455 let (expected_text, expected_selections) = marked_text_ranges(indoc! {"
10456 aaaa
10457 bX|bbXb
10458 bX|bbX|b
10459 cccc"});
10460 assert_eq!(view.text(cx), expected_text);
10461 assert_eq!(view.selections.ranges(cx), expected_selections);
10462
10463 view.newline(&Newline, cx);
10464 let (expected_text, expected_selections) = marked_text_ranges(indoc! {"
10465 aaaa
10466 bX
10467 |bbX
10468 b
10469 bX
10470 |bbX
10471 |b
10472 cccc"});
10473 assert_eq!(view.text(cx), expected_text);
10474 assert_eq!(view.selections.ranges(cx), expected_selections);
10475 });
10476 }
10477
10478 #[gpui::test]
10479 fn test_refresh_selections(cx: &mut gpui::MutableAppContext) {
10480 cx.set_global(Settings::test(cx));
10481 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
10482 let mut excerpt1_id = None;
10483 let multibuffer = cx.add_model(|cx| {
10484 let mut multibuffer = MultiBuffer::new(0);
10485 excerpt1_id = multibuffer
10486 .push_excerpts(
10487 buffer.clone(),
10488 [
10489 ExcerptRange {
10490 context: Point::new(0, 0)..Point::new(1, 4),
10491 primary: None,
10492 },
10493 ExcerptRange {
10494 context: Point::new(1, 0)..Point::new(2, 4),
10495 primary: None,
10496 },
10497 ],
10498 cx,
10499 )
10500 .into_iter()
10501 .next();
10502 multibuffer
10503 });
10504 assert_eq!(
10505 multibuffer.read(cx).read(cx).text(),
10506 "aaaa\nbbbb\nbbbb\ncccc"
10507 );
10508 let (_, editor) = cx.add_window(Default::default(), |cx| {
10509 let mut editor = build_editor(multibuffer.clone(), cx);
10510 let snapshot = editor.snapshot(cx);
10511 editor.change_selections(None, cx, |s| {
10512 s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
10513 });
10514 editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
10515 assert_eq!(
10516 editor.selections.ranges(cx),
10517 [
10518 Point::new(1, 3)..Point::new(1, 3),
10519 Point::new(2, 1)..Point::new(2, 1),
10520 ]
10521 );
10522 editor
10523 });
10524
10525 // Refreshing selections is a no-op when excerpts haven't changed.
10526 editor.update(cx, |editor, cx| {
10527 editor.change_selections(None, cx, |s| {
10528 s.refresh();
10529 });
10530 assert_eq!(
10531 editor.selections.ranges(cx),
10532 [
10533 Point::new(1, 3)..Point::new(1, 3),
10534 Point::new(2, 1)..Point::new(2, 1),
10535 ]
10536 );
10537 });
10538
10539 multibuffer.update(cx, |multibuffer, cx| {
10540 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
10541 });
10542 editor.update(cx, |editor, cx| {
10543 // Removing an excerpt causes the first selection to become degenerate.
10544 assert_eq!(
10545 editor.selections.ranges(cx),
10546 [
10547 Point::new(0, 0)..Point::new(0, 0),
10548 Point::new(0, 1)..Point::new(0, 1)
10549 ]
10550 );
10551
10552 // Refreshing selections will relocate the first selection to the original buffer
10553 // location.
10554 editor.change_selections(None, cx, |s| {
10555 s.refresh();
10556 });
10557 assert_eq!(
10558 editor.selections.ranges(cx),
10559 [
10560 Point::new(0, 1)..Point::new(0, 1),
10561 Point::new(0, 3)..Point::new(0, 3)
10562 ]
10563 );
10564 assert!(editor.selections.pending_anchor().is_some());
10565 });
10566 }
10567
10568 #[gpui::test]
10569 fn test_refresh_selections_while_selecting_with_mouse(cx: &mut gpui::MutableAppContext) {
10570 cx.set_global(Settings::test(cx));
10571 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
10572 let mut excerpt1_id = None;
10573 let multibuffer = cx.add_model(|cx| {
10574 let mut multibuffer = MultiBuffer::new(0);
10575 excerpt1_id = multibuffer
10576 .push_excerpts(
10577 buffer.clone(),
10578 [
10579 ExcerptRange {
10580 context: Point::new(0, 0)..Point::new(1, 4),
10581 primary: None,
10582 },
10583 ExcerptRange {
10584 context: Point::new(1, 0)..Point::new(2, 4),
10585 primary: None,
10586 },
10587 ],
10588 cx,
10589 )
10590 .into_iter()
10591 .next();
10592 multibuffer
10593 });
10594 assert_eq!(
10595 multibuffer.read(cx).read(cx).text(),
10596 "aaaa\nbbbb\nbbbb\ncccc"
10597 );
10598 let (_, editor) = cx.add_window(Default::default(), |cx| {
10599 let mut editor = build_editor(multibuffer.clone(), cx);
10600 let snapshot = editor.snapshot(cx);
10601 editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
10602 assert_eq!(
10603 editor.selections.ranges(cx),
10604 [Point::new(1, 3)..Point::new(1, 3)]
10605 );
10606 editor
10607 });
10608
10609 multibuffer.update(cx, |multibuffer, cx| {
10610 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
10611 });
10612 editor.update(cx, |editor, cx| {
10613 assert_eq!(
10614 editor.selections.ranges(cx),
10615 [Point::new(0, 0)..Point::new(0, 0)]
10616 );
10617
10618 // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
10619 editor.change_selections(None, cx, |s| {
10620 s.refresh();
10621 });
10622 assert_eq!(
10623 editor.selections.ranges(cx),
10624 [Point::new(0, 3)..Point::new(0, 3)]
10625 );
10626 assert!(editor.selections.pending_anchor().is_some());
10627 });
10628 }
10629
10630 #[gpui::test]
10631 async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
10632 cx.update(|cx| cx.set_global(Settings::test(cx)));
10633 let language = Arc::new(
10634 Language::new(
10635 LanguageConfig {
10636 brackets: vec![
10637 BracketPair {
10638 start: "{".to_string(),
10639 end: "}".to_string(),
10640 close: true,
10641 newline: true,
10642 },
10643 BracketPair {
10644 start: "/* ".to_string(),
10645 end: " */".to_string(),
10646 close: true,
10647 newline: true,
10648 },
10649 ],
10650 ..Default::default()
10651 },
10652 Some(tree_sitter_rust::language()),
10653 )
10654 .with_indents_query("")
10655 .unwrap(),
10656 );
10657
10658 let text = concat!(
10659 "{ }\n", // Suppress rustfmt
10660 " x\n", //
10661 " /* */\n", //
10662 "x\n", //
10663 "{{} }\n", //
10664 );
10665
10666 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
10667 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10668 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
10669 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
10670 .await;
10671
10672 view.update(cx, |view, cx| {
10673 view.change_selections(None, cx, |s| {
10674 s.select_display_ranges([
10675 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
10676 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
10677 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
10678 ])
10679 });
10680 view.newline(&Newline, cx);
10681
10682 assert_eq!(
10683 view.buffer().read(cx).read(cx).text(),
10684 concat!(
10685 "{ \n", // Suppress rustfmt
10686 "\n", //
10687 "}\n", //
10688 " x\n", //
10689 " /* \n", //
10690 " \n", //
10691 " */\n", //
10692 "x\n", //
10693 "{{} \n", //
10694 "}\n", //
10695 )
10696 );
10697 });
10698 }
10699
10700 #[gpui::test]
10701 fn test_highlighted_ranges(cx: &mut gpui::MutableAppContext) {
10702 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10703
10704 cx.set_global(Settings::test(cx));
10705 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
10706
10707 editor.update(cx, |editor, cx| {
10708 struct Type1;
10709 struct Type2;
10710
10711 let buffer = buffer.read(cx).snapshot(cx);
10712
10713 let anchor_range = |range: Range<Point>| {
10714 buffer.anchor_after(range.start)..buffer.anchor_after(range.end)
10715 };
10716
10717 editor.highlight_background::<Type1>(
10718 vec![
10719 anchor_range(Point::new(2, 1)..Point::new(2, 3)),
10720 anchor_range(Point::new(4, 2)..Point::new(4, 4)),
10721 anchor_range(Point::new(6, 3)..Point::new(6, 5)),
10722 anchor_range(Point::new(8, 4)..Point::new(8, 6)),
10723 ],
10724 |_| Color::red(),
10725 cx,
10726 );
10727 editor.highlight_background::<Type2>(
10728 vec![
10729 anchor_range(Point::new(3, 2)..Point::new(3, 5)),
10730 anchor_range(Point::new(5, 3)..Point::new(5, 6)),
10731 anchor_range(Point::new(7, 4)..Point::new(7, 7)),
10732 anchor_range(Point::new(9, 5)..Point::new(9, 8)),
10733 ],
10734 |_| Color::green(),
10735 cx,
10736 );
10737
10738 let snapshot = editor.snapshot(cx);
10739 let mut highlighted_ranges = editor.background_highlights_in_range(
10740 anchor_range(Point::new(3, 4)..Point::new(7, 4)),
10741 &snapshot,
10742 cx.global::<Settings>().theme.as_ref(),
10743 );
10744 // Enforce a consistent ordering based on color without relying on the ordering of the
10745 // highlight's `TypeId` which is non-deterministic.
10746 highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
10747 assert_eq!(
10748 highlighted_ranges,
10749 &[
10750 (
10751 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
10752 Color::green(),
10753 ),
10754 (
10755 DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
10756 Color::green(),
10757 ),
10758 (
10759 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
10760 Color::red(),
10761 ),
10762 (
10763 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
10764 Color::red(),
10765 ),
10766 ]
10767 );
10768 assert_eq!(
10769 editor.background_highlights_in_range(
10770 anchor_range(Point::new(5, 6)..Point::new(6, 4)),
10771 &snapshot,
10772 cx.global::<Settings>().theme.as_ref(),
10773 ),
10774 &[(
10775 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
10776 Color::red(),
10777 )]
10778 );
10779 });
10780 }
10781
10782 #[gpui::test]
10783 fn test_following(cx: &mut gpui::MutableAppContext) {
10784 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10785
10786 cx.set_global(Settings::test(cx));
10787
10788 let (_, leader) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
10789 let (_, follower) = cx.add_window(
10790 WindowOptions {
10791 bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
10792 ..Default::default()
10793 },
10794 |cx| build_editor(buffer.clone(), cx),
10795 );
10796
10797 let pending_update = Rc::new(RefCell::new(None));
10798 follower.update(cx, {
10799 let update = pending_update.clone();
10800 |_, cx| {
10801 cx.subscribe(&leader, move |_, leader, event, cx| {
10802 leader
10803 .read(cx)
10804 .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
10805 })
10806 .detach();
10807 }
10808 });
10809
10810 // Update the selections only
10811 leader.update(cx, |leader, cx| {
10812 leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
10813 });
10814 follower.update(cx, |follower, cx| {
10815 follower
10816 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10817 .unwrap();
10818 });
10819 assert_eq!(follower.read(cx).selections.ranges(cx), vec![1..1]);
10820
10821 // Update the scroll position only
10822 leader.update(cx, |leader, cx| {
10823 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
10824 });
10825 follower.update(cx, |follower, cx| {
10826 follower
10827 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10828 .unwrap();
10829 });
10830 assert_eq!(
10831 follower.update(cx, |follower, cx| follower.scroll_position(cx)),
10832 vec2f(1.5, 3.5)
10833 );
10834
10835 // Update the selections and scroll position
10836 leader.update(cx, |leader, cx| {
10837 leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
10838 leader.request_autoscroll(Autoscroll::Newest, cx);
10839 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
10840 });
10841 follower.update(cx, |follower, cx| {
10842 let initial_scroll_position = follower.scroll_position(cx);
10843 follower
10844 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10845 .unwrap();
10846 assert_eq!(follower.scroll_position(cx), initial_scroll_position);
10847 assert!(follower.autoscroll_request.is_some());
10848 });
10849 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0]);
10850
10851 // Creating a pending selection that precedes another selection
10852 leader.update(cx, |leader, cx| {
10853 leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
10854 leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
10855 });
10856 follower.update(cx, |follower, cx| {
10857 follower
10858 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10859 .unwrap();
10860 });
10861 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0, 1..1]);
10862
10863 // Extend the pending selection so that it surrounds another selection
10864 leader.update(cx, |leader, cx| {
10865 leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
10866 });
10867 follower.update(cx, |follower, cx| {
10868 follower
10869 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10870 .unwrap();
10871 });
10872 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..2]);
10873 }
10874
10875 #[test]
10876 fn test_combine_syntax_and_fuzzy_match_highlights() {
10877 let string = "abcdefghijklmnop";
10878 let syntax_ranges = [
10879 (
10880 0..3,
10881 HighlightStyle {
10882 color: Some(Color::red()),
10883 ..Default::default()
10884 },
10885 ),
10886 (
10887 4..8,
10888 HighlightStyle {
10889 color: Some(Color::green()),
10890 ..Default::default()
10891 },
10892 ),
10893 ];
10894 let match_indices = [4, 6, 7, 8];
10895 assert_eq!(
10896 combine_syntax_and_fuzzy_match_highlights(
10897 &string,
10898 Default::default(),
10899 syntax_ranges.into_iter(),
10900 &match_indices,
10901 ),
10902 &[
10903 (
10904 0..3,
10905 HighlightStyle {
10906 color: Some(Color::red()),
10907 ..Default::default()
10908 },
10909 ),
10910 (
10911 4..5,
10912 HighlightStyle {
10913 color: Some(Color::green()),
10914 weight: Some(fonts::Weight::BOLD),
10915 ..Default::default()
10916 },
10917 ),
10918 (
10919 5..6,
10920 HighlightStyle {
10921 color: Some(Color::green()),
10922 ..Default::default()
10923 },
10924 ),
10925 (
10926 6..8,
10927 HighlightStyle {
10928 color: Some(Color::green()),
10929 weight: Some(fonts::Weight::BOLD),
10930 ..Default::default()
10931 },
10932 ),
10933 (
10934 8..9,
10935 HighlightStyle {
10936 weight: Some(fonts::Weight::BOLD),
10937 ..Default::default()
10938 },
10939 ),
10940 ]
10941 );
10942 }
10943
10944 fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
10945 let point = DisplayPoint::new(row as u32, column as u32);
10946 point..point
10947 }
10948
10949 fn assert_selection_ranges(
10950 marked_text: &str,
10951 selection_marker_pairs: Vec<(char, char)>,
10952 view: &mut Editor,
10953 cx: &mut ViewContext<Editor>,
10954 ) {
10955 let snapshot = view.snapshot(cx).display_snapshot;
10956 let mut marker_chars = Vec::new();
10957 for (start, end) in selection_marker_pairs.iter() {
10958 marker_chars.push(*start);
10959 marker_chars.push(*end);
10960 }
10961 let (_, markers) = marked_text_by(marked_text, marker_chars);
10962 let asserted_ranges: Vec<Range<DisplayPoint>> = selection_marker_pairs
10963 .iter()
10964 .map(|(start, end)| {
10965 let start = markers.get(start).unwrap()[0].to_display_point(&snapshot);
10966 let end = markers.get(end).unwrap()[0].to_display_point(&snapshot);
10967 start..end
10968 })
10969 .collect();
10970 assert_eq!(
10971 view.selections.display_ranges(cx),
10972 &asserted_ranges[..],
10973 "Assert selections are {}",
10974 marked_text
10975 );
10976 }
10977}
10978
10979trait RangeExt<T> {
10980 fn sorted(&self) -> Range<T>;
10981 fn to_inclusive(&self) -> RangeInclusive<T>;
10982}
10983
10984impl<T: Ord + Clone> RangeExt<T> for Range<T> {
10985 fn sorted(&self) -> Self {
10986 cmp::min(&self.start, &self.end).clone()..cmp::max(&self.start, &self.end).clone()
10987 }
10988
10989 fn to_inclusive(&self) -> RangeInclusive<T> {
10990 self.start.clone()..=self.end.clone()
10991 }
10992}
10993
10994trait RangeToAnchorExt {
10995 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
10996}
10997
10998impl<T: ToOffset> RangeToAnchorExt for Range<T> {
10999 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
11000 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
11001 }
11002}