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